Browse Source

Merge branch 'dev' into add-material-stencil

Garrett Johnson 6 years ago
parent
commit
83180aed41
100 changed files with 6280 additions and 6212 deletions
  1. 0 1
      .editorconfig
  2. 1 1
      .github/ISSUE_TEMPLATE.md
  3. 8 1
      build/three.js
  4. 361 361
      build/three.min.js
  5. 8 1
      build/three.module.js
  6. 1 1
      docs/api/en/animation/KeyframeTrack.html
  7. 2 2
      docs/api/en/core/Clock.html
  8. 7 0
      docs/api/en/renderers/WebGLRenderer.html
  9. 1 1
      docs/api/zh/animation/KeyframeTrack.html
  10. 2 2
      docs/api/zh/core/Clock.html
  11. 138 0
      docs/examples/loaders/BasisTextureLoader.html
  12. 18 18
      docs/examples/math/convexhull/ConvexHull.html
  13. 2 2
      docs/examples/math/convexhull/Face.html
  14. 2 2
      docs/examples/math/convexhull/HalfEdge.html
  15. 2 2
      docs/examples/math/convexhull/VertexList.html
  16. 2 2
      docs/examples/math/convexhull/VertexNode.html
  17. 7 0
      docs/examples/renderers/SVGRenderer.html
  18. 13 12
      docs/index.html
  19. 7 6
      docs/list.js
  20. 1 1
      docs/manual/en/introduction/Creating-a-scene.html
  21. 1 1
      docs/manual/en/introduction/Drawing-lines.html
  22. 0 2
      docs/manual/en/introduction/How-to-update-things.html
  23. 3 1
      docs/manual/en/introduction/How-to-use-WebGL2.html
  24. 72 0
      docs/manual/en/introduction/Import-via-modules.html
  25. 1 4
      docs/manual/en/introduction/Useful-links.html
  26. 127 95
      docs/page.css
  27. 0 2
      docs/prettify/threejs.css
  28. 1 1
      docs/scenes/geometry-browser.html
  29. 9 1
      editor/sw.js
  30. 3 24
      examples/css2d_label.html
  31. 21 45
      examples/css3d_molecules.html
  32. 6 17
      examples/css3d_orthographic.html
  33. 1 24
      examples/css3d_panorama.html
  34. 2 25
      examples/css3d_panorama_deviceorientation.html
  35. 7 26
      examples/css3d_periodictable.html
  36. 4 20
      examples/css3d_sandbox.html
  37. 5 24
      examples/css3d_sprites.html
  38. 10 9
      examples/css3d_youtube.html
  39. 1 0
      examples/files.js
  40. 9 28
      examples/index.html
  41. 0 71
      examples/js/ImprovedNoise.js
  42. 0 23
      examples/js/PRNG.js
  43. 0 332
      examples/js/ShaderGodRays.js
  44. 0 694
      examples/js/ShaderSkin.js
  45. 0 324
      examples/js/ShaderTerrain.js
  46. 0 331
      examples/js/ShaderToon.js
  47. 0 324
      examples/js/SimplexNoise.js
  48. 0 0
      examples/js/animation/AnimationClipCreator.js
  49. 21 27
      examples/js/animation/TimelinerController.js
  50. 0 39
      examples/js/crossfade/gui.js
  51. 0 119
      examples/js/crossfade/scenes.js
  52. 0 168
      examples/js/crossfade/transition.js
  53. 19 19
      examples/js/curves/CurveExtras.js
  54. 6 0
      examples/js/effects/AsciiEffect.js
  55. 11 14
      examples/js/effects/OutlineEffect.js
  56. 13 13
      examples/js/geometries/BoxLineGeometry.js
  57. 36 47
      examples/js/geometries/ConvexGeometry.js
  58. 202 210
      examples/js/geometries/DecalGeometry.js
  59. 0 157
      examples/js/geometries/Hilbert.js
  60. 3 3
      examples/js/geometries/LightningStrike.js
  61. 0 0
      examples/js/geometries/ParametricGeometries.js
  62. 1 4
      examples/js/geometries/TeapotBufferGeometry.js
  63. 4 4
      examples/js/libs/basis/README.md
  64. 0 0
      examples/js/libs/basis/basis_transcoder.js
  65. BIN
      examples/js/libs/basis/basis_transcoder.wasm
  66. 1 1
      examples/js/lines/Line2.js
  67. 1 1
      examples/js/lines/LineGeometry.js
  68. 1 1
      examples/js/lines/LineSegments2.js
  69. 1 3
      examples/js/lines/LineSegmentsGeometry.js
  70. 1 1
      examples/js/lines/Wireframe.js
  71. 1 1
      examples/js/lines/WireframeGeometry2.js
  72. 27 132
      examples/js/loaders/AssimpLoader.js
  73. 174 90
      examples/js/loaders/BasisTextureLoader.js
  74. 10 21
      examples/js/loaders/EXRLoader.js
  75. 60 2
      examples/js/loaders/GLTFLoader.js
  76. 321 303
      examples/js/loaders/LDrawLoader.js
  77. 2237 1634
      examples/js/loaders/LWOLoader.js
  78. 8 12
      examples/js/loaders/PRWMLoader.js
  79. 158 30
      examples/js/loaders/RGBELoader.js
  80. 7 7
      examples/js/loaders/SVGLoader.js
  81. 188 186
      examples/js/loaders/TDSLoader.js
  82. 15 3
      examples/js/loaders/VTKLoader.js
  83. 6 9
      examples/js/math/ConvexHull.js
  84. 72 0
      examples/js/math/ImprovedNoise.js
  85. 0 15
      examples/js/math/Lut.js
  86. 405 0
      examples/js/math/SimplexNoise.js
  87. 4 5
      examples/js/modifiers/SubdivisionModifier.js
  88. 2 2
      examples/js/modifiers/TessellateModifier.js
  89. 7 1
      examples/js/nodes/core/FunctionNode.js
  90. 0 36
      examples/js/objects/Fire.js
  91. 4 0
      examples/js/objects/ReflectorRTT.js
  92. 10 10
      examples/js/objects/ShadowMesh.js
  93. 2 4
      examples/js/postprocessing/EffectComposer.js
  94. 2 2
      examples/js/postprocessing/SSAOPass.js
  95. 3 4
      examples/js/renderers/RaytracingRenderer.js
  96. 28 0
      examples/js/renderers/SVGRenderer.js
  97. 8 8
      examples/js/renderers/WebGLDeferredRenderer.js
  98. 328 0
      examples/js/shaders/GodRaysShader.js
  99. 686 0
      examples/js/shaders/SkinShader.js
  100. 320 0
      examples/js/shaders/TerrainShader.js

+ 0 - 1
.editorconfig

@@ -9,7 +9,6 @@ insert_final_newline = true
 [*.{js,ts,html}]
 charset = utf-8
 indent_style = tab
-indent_size = 2
 
 [*.{js,ts}]
 trim_trailing_whitespace = true

+ 1 - 1
.github/ISSUE_TEMPLATE.md

@@ -19,7 +19,7 @@ Please also include a live example if possible. You can start from these templat
 ##### Three.js version
 
 - [ ] Dev
-- [ ] r104
+- [ ] r105
 - [ ] ...
 
 ##### Browser

File diff suppressed because it is too large
+ 8 - 1
build/three.js


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


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


+ 1 - 1
docs/api/en/animation/KeyframeTrack.html

@@ -58,7 +58,7 @@
 
 		<p>
 			Some examples of how to manually create [page:AnimationClip AnimationClips] with different sorts
-			of KeyframeTracks can be found in the [link:https://threejs.org/examples/js/AnimationClipCreator.js]
+			of KeyframeTracks can be found in the [link:https://threejs.org/examples/js/animation/AnimationClipCreator.js AnimationClipCreator]
 			file.
 		</p>
 

+ 2 - 2
docs/api/en/core/Clock.html

@@ -11,8 +11,8 @@
 		<h1>[name]</h1>
 
 		<p class="desc">
-		Object for keeping track of time. This uses <a href="https://developer.mozilla.org/en-US/docs/Web/API/Performance/now">performance.now()</a>
-		if it is available, otherwise it reverts to the less accurate <a href="https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Date/now">Date.now()</a>.
+		Object for keeping track of time. This uses [link:https://developer.mozilla.org/en-US/docs/Web/API/Performance/now performance.now]
+		if it is available, otherwise it reverts to the less accurate [link:https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Date/now Date.now].
 		</p>
 
 

+ 7 - 0
docs/api/en/renderers/WebGLRenderer.html

@@ -113,6 +113,7 @@
 		- [page:Boolean floatVertexTextures]: *true* if [page:Boolean floatFragmentTextures] and [page:Boolean vertexTextures] are both true.<br />
 		- [page:Method getMaxAnisotropy](): Returns the maximum available anisotropy.<br />
 		- [page:Method getMaxPrecision](): Returns the maximum available precision for vertex and fragment shaders. <br />
+		- [page:Boolean isWebGL2]: *true* if the context in use is a WebGL2RenderingContext object.<br />
 		- [page:Boolean logarithmicDepthBuffer]: *true* if the [page:parameter logarithmicDepthBuffer] was set to true in the constructor and
 		the context supports the [link:https://developer.mozilla.org/en-US/docs/Web/API/EXT_frag_depth EXT_frag_depth] extension.
 			According to [link:https://webglstats.com/ WebGLStats], as of February 2016 around 66% of WebGL enabled devices support this.<br />
@@ -350,6 +351,12 @@
 		<h3>[method:WebGLContextAttributes getContextAttributes]()</h3>
 		<p>Returns an object that describes the attributes set on the WebGL context when it was created.</p>
 
+		<h3>[method:Integer getActiveCubeFace]()</h3>
+		<p>Returns the current active cube face.</p>
+
+		<h3>[method:Integer getActiveMipMapLevel]()</h3>
+		<p>Returns the current active mipmap level.</p>
+
 		<h3>[method:RenderTarget getRenderTarget]()</h3>
 		<p>Returns the current [page:RenderTarget RenderTarget] if there are; returns *null* otherwise.</p>
 

+ 1 - 1
docs/api/zh/animation/KeyframeTrack.html

@@ -49,7 +49,7 @@
 		</ul>
 
 		<p>
-            可以在[link:https://threejs.org/examples/js/AnimationClipCreator.js]文件中找到用不同类型的关键帧轨道创建动画剪辑([page:AnimationClip AnimationClips])的示例。
+            可以在[link:https://threejs.org/examples/js/animation/AnimationClipCreator.js AnimationClipCreator]文件中找到用不同类型的关键帧轨道创建动画剪辑([page:AnimationClip AnimationClips])的示例。
 		</p>
 
 		<p>

+ 2 - 2
docs/api/zh/core/Clock.html

@@ -10,8 +10,8 @@
 	<body>
 		<h1>[name]</h1>
 		<p class="desc">
-			该对象用于跟踪时间。如果<a href="https://developer.mozilla.org/en-US/docs/Web/API/Performance/now">performance.now()</a>可用,则
-			Clock 对象通过该方法实现,否则通过歉精准的<a href="https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Date/now">Date.now()</a>实现。
+			该对象用于跟踪时间。如果[link:https://developer.mozilla.org/en-US/docs/Web/API/Performance/now performance.now]可用,则
+			Clock 对象通过该方法实现,否则通过歉精准的[link:https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Date/now Date.now]实现。
 		</p>
 
 

+ 138 - 0
docs/examples/loaders/BasisTextureLoader.html

@@ -0,0 +1,138 @@
+<!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:Loader] &rarr;
+		<h1>[name]</h1>
+
+		<p class="desc">
+			Loader for Basis Universal GPU Texture Codec.<br><br>
+
+			[link:https://github.com/BinomialLLC/basis_universal/ Basis Universal] is a
+			"supercompressed" GPU texture and texture video compression system that
+			outputs a highly compressed intermediate file format (.basis) that can be
+			quickly transcoded to a wide variety of GPU texture compression formats.
+		</p>
+
+		<p>
+			This loader parallelizes the transcoding process across a configurable number
+			of web workers, before transferring the transcoded compressed texture back
+			to the main thread. The required WASM transcoder and JS wrapper are available from the
+			[link:https://github.com/mrdoob/three.js/tree/dev/examples/js/libs/basis examples/js/libs/basis]
+			directory.
+		</p>
+
+		<h2>Example</h2>
+
+		<code>
+		var basisLoader = new THREE.BasisTextureLoader();
+		basisLoader.setTranscoderPath( 'examples/js/libs/basis/' );
+		basisLoader.detectSupport( renderer );
+		basisLoader.load( 'diffuse.basis', function ( texture ) {
+
+			var material = new THREE.MeshStandardMaterial( { map: texture } );
+
+		}, function () {
+
+			console.log( 'onProgress' );
+
+		}, function ( e ) {
+
+			console.error( e );
+
+		} );
+		</code>
+
+		[example:webgl_loader_texture_basis]
+
+		<h2>Browser compatibility</h2>
+
+		<p>
+			BasisTextureLoader transcodes input textures in '.basis' format to an
+			appropriate compressed texture format for the target device, where
+			possible. This allows the same source texture to be served across
+			desktop, Android, and iOS devices, and transcoded into DXT, ETC1, or
+			PVRTC1. Other output formats may be supported in the future.
+		</p>
+		<p>
+			This loader relies on ES6 Promises and Web Assembly, which are not
+			supported in IE11.
+		</p>
+
+		<br>
+		<hr>
+
+		<h2>Constructor</h2>
+
+		<h3>[name]( [param:LoadingManager manager] )</h3>
+		<p>
+		[page:LoadingManager manager] — The [page:LoadingManager] for the loader to use. Default is [page:LoadingManager THREE.DefaultLoadingManager].
+		</p>
+		<p>
+		Creates a new [name].
+		</p>
+
+		<h2>Methods</h2>
+
+		<h3>[method:null load]( [param:String url], [param:Function onLoad], [param:Function onProgress], [param:Function onError] )</h3>
+		<p>
+		[page:String url] — A string containing the path/URL of the <em>.basis</em> file.<br />
+		[page:Function onLoad] — A function to be called after the loading is successfully completed.<br />
+		[page:Function onProgress] — (optional) A function to be called while the loading is in progress. The argument will be the XMLHttpRequest instance, that contains .[page:Integer total] and .[page:Integer loaded] bytes.<br />
+		[page:Function onError] — (optional) A function to be called if an error occurs during loading. The function receives error as an argument.<br />
+		</p>
+		<p>
+		Load from url and call the <em>onLoad</em> function with the transcoded [page:CompressedTexture].
+		</p>
+
+		<h3>[method:this detectSupport]( [param:WebGLRenderer renderer] )</h3>
+		<p>
+		[page:WebGLRenderer renderer] — A renderer instance.
+		</p>
+		<p>
+		Detects hardware support for available compressed texture formats, to determine
+		the output format for the transcoder. Must be called before loading a texture.
+		</p>
+
+		<h3>[method:this setCrossOrigin]( [param:String crossOrigin] )</h3>
+		<p>
+		[page:String crossOrigin] — Options are '', 'anonymous', or 'use-credentials'. Default is 'anonymous'.
+		</p>
+		<p>
+		Sets options for CORS requests.
+		</p>
+
+		<h3>[method:this setTranscoderPath]( [param:String path] )</h3>
+		<p>
+		[page:String path] — Path to folder containing the WASM transcoder and JS wrapper.
+		</p>
+		<p>
+		The WASM transcoder and JS wrapper are available from the
+		[link:https://github.com/mrdoob/three.js/tree/dev/examples/js/libs/basis examples/js/libs/basis]
+		directory.
+		</p>
+
+		<h3>[method:this setWorkerLimit]( [param:Number limit] )</h3>
+		<p>
+		[page:Number limit] — Maximum number of workers. Default is '4'.
+		</p>
+		<p>
+		Sets the maximum number of web workers to be allocated by this instance.
+		</p>
+
+		<h3>[method:this dispose]()</h3>
+		<p>
+		Disposes the loader object, de-allocating any Web Workers created.
+		</p>
+
+		<h2>Source</h2>
+
+		[link:https://github.com/mrdoob/three.js/blob/master/examples/js/loaders/BasisTextureLoader.js examples/js/loaders/BasisTextureLoader.js]
+	</body>
+</html>

+ 18 - 18
docs/examples/quickhull/QuickHull.html → docs/examples/math/convexhull/ConvexHull.html

@@ -2,7 +2,7 @@
 <html lang="en">
 	<head>
 		<meta charset="utf-8" />
-		<base href="../../" />
+		<base href="../../../" />
 		<script src="list.js"></script>
 		<script src="page.js"></script>
 		<link type="text/css" rel="stylesheet" href="page.css" />
@@ -11,7 +11,7 @@
 		<h1>[name]</h1>
 
 		<p class="desc">
-			General information about the Quickhull algorithm: Dirk Gregorius. March 2014, Game Developers Conference: [link:http://media.steampowered.com/apps/valve/2014/DirkGregorius_ImplementingQuickHull.pdf Implementing QuickHull].
+			A convex hull class. Implements the Quickhull algorithm by: Dirk Gregorius. March 2014, Game Developers Conference: [link:http://media.steampowered.com/apps/valve/2014/DirkGregorius_ImplementingQuickHull.pdf Implementing QuickHull].
 		</p>
 
 
@@ -63,19 +63,19 @@
 		<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 addNewFaces]( [param:VertexNode eyeVertex], [param:HalfEdge horizonEdge] )</h3>
+		<h3>[method:ConvexHull 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>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>
+		<h3>[method:ConvexHull addVertexToFace]( [param:VertexNode vertex], [param:Face face]	)</h3>
 		[page:VertexNodeNode vertex] - The vertex to add.<br /><br />
 		[page:Face face] - The target face.<br /><br />
 
 		<p>Adds a vertex to the 'assigned' list of vertices and assigns it to the given face.</p>
 
-		<h3>[method:QuickHull addVertexToHull]( [param:VertexNode eyeVertex] )</h3>
+		<h3>[method:ConvexHull addVertexToHull]( [param:VertexNode eyeVertex] )</h3>
 		[page:VertexNode eyeVertex] - The vertex that is added to the hull.<br /><br />
 
 		<p>Adds a vertex to the hull with the following algorithm
@@ -87,11 +87,11 @@
 			</ul>
 		</p>
 
-		<h3>[method:QuickHull cleanup]()</h3>
+		<h3>[method:ConvexHull cleanup]()</h3>
 
 		<p>Cleans up internal properties after computing the convex hull.</p>
 
-		<h3>[method:QuickHull compute]()</h3>
+		<h3>[method:ConvexHull compute]()</h3>
 
 		<p>Starts the execution of the quick hull algorithm.</p>
 
@@ -99,7 +99,7 @@
 
 		<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>
+		<h3>[method:ConvexHull 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 />
@@ -107,16 +107,16 @@
 
 		<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>
+		<h3>[method:ConvexHull 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>
+		<h3>[method:ConvexHull 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>
+		<h3>[method:ConvexHull deleteFaceVertices]( [param:Face face], [param:Face absorbingFace]	)</h3>
 		[page:Face face] - The given face.<br /><br />
 		[page:Face absorbingFace] - An optional face that tries to absorb the vertices of the first face.<br /><br />
 
@@ -139,7 +139,7 @@
 
 		<p>Returns *true* if the given ray intersects with this convex hull.</p>
 
-		<h3>[method:QuickHull makeEmpty]()</h3>
+		<h3>[method:ConvexHull makeEmpty]()</h3>
 
 		<p>Makes this convex hull empty.</p>
 
@@ -153,7 +153,7 @@
 			</ul>
 		</p>
 
-		<h3>[method:QuickHull reindexFaces]()</h3>
+		<h3>[method:ConvexHull reindexFaces]()</h3>
 
 		<p>Removes inactive (e.g. deleted) faces from the internal face list.</p>
 
@@ -162,30 +162,30 @@
 
 		<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 removeVertexFromFace]( [param:VertexNode vertex], [param:Face face]	)</h3>
+		<h3>[method:ConvexHull 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>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 resolveUnassignedPoints]( [param:Array newFaces]	)</h3>
+		<h3>[method:ConvexHull resolveUnassignedPoints]( [param:Array newFaces]	)</h3>
 		[page:Face newFaces] - An array of new faces.<br /><br />
 
 		<p>Reassigns as many vertices as possible from the unassigned list to the new faces.</p>
 
-		<h3>[method:QuickHull setFromObject]( [param:Object3D object] )</h3>
+		<h3>[method:ConvexHull setFromObject]( [param:Object3D object] )</h3>
 		[page:Object3D object] - [page:Object3D] to compute the convex hull of.<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>
 
-		<h3>[method:QuickHull setFromPoints]( [param:Array points] )</h3>
+		<h3>[method:ConvexHull 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>
 
 		<h2>Source</h2>
 
-		[link:https://github.com/mrdoob/three.js/blob/master/examples/js/QuickHull.js examples/js/QuickHull.js]
+		[link:https://github.com/mrdoob/three.js/blob/master/examples/js/math/ConvexHull.js examples/js/ConvexHull.js]
 	</body>
 </html>

+ 2 - 2
docs/examples/quickhull/Face.html → docs/examples/math/convexhull/Face.html

@@ -2,7 +2,7 @@
 <html lang="en">
 	<head>
 		<meta charset="utf-8" />
-		<base href="../../" />
+		<base href="../../../" />
 		<script src="list.js"></script>
 		<script src="page.js"></script>
 		<link type="text/css" rel="stylesheet" href="page.css" />
@@ -84,6 +84,6 @@
 
 		<h2>Source</h2>
 
-		[link:https://github.com/mrdoob/three.js/blob/master/examples/js/QuickHull.js examples/js/QuickHull.js]
+		[link:https://github.com/mrdoob/three.js/blob/master/examples/js/math/ConvexHull.js examples/js/math/ConvexHull.js]
 	</body>
 </html>

+ 2 - 2
docs/examples/quickhull/HalfEdge.html → docs/examples/math/convexhull/HalfEdge.html

@@ -2,7 +2,7 @@
 <html lang="en">
 	<head>
 		<meta charset="utf-8" />
-		<base href="../../" />
+		<base href="../../../" />
 		<script src="list.js"></script>
 		<script src="page.js"></script>
 		<link type="text/css" rel="stylesheet" href="page.css" />
@@ -74,6 +74,6 @@
 
 		<h2>Source</h2>
 
-		[link:https://github.com/mrdoob/three.js/blob/master/examples/js/QuickHull.js examples/js/QuickHull.js]
+		[link:https://github.com/mrdoob/three.js/blob/master/examples/js/math/ConvexHull.js examples/js/math/ConvexHull.js]
 	</body>
 </html>

+ 2 - 2
docs/examples/quickhull/VertexList.html → docs/examples/math/convexhull/VertexList.html

@@ -2,7 +2,7 @@
 <html lang="en">
 	<head>
 		<meta charset="utf-8" />
-		<base href="../../" />
+		<base href="../../../" />
 		<script src="list.js"></script>
 		<script src="page.js"></script>
 		<link type="text/css" rel="stylesheet" href="page.css" />
@@ -86,6 +86,6 @@
 
 		<h2>Source</h2>
 
-		[link:https://github.com/mrdoob/three.js/blob/master/examples/js/QuickHull.js examples/js/QuickHull.js]
+		[link:https://github.com/mrdoob/three.js/blob/master/examples/js/math/ConvexHull.js examples/js/math/ConvexHull.js]
 	</body>
 </html>

+ 2 - 2
docs/examples/quickhull/VertexNode.html → docs/examples/math/convexhull/VertexNode.html

@@ -2,7 +2,7 @@
 <html lang="en">
 	<head>
 		<meta charset="utf-8" />
-		<base href="../../" />
+		<base href="../../../" />
 		<script src="list.js"></script>
 		<script src="page.js"></script>
 		<link type="text/css" rel="stylesheet" href="page.css" />
@@ -47,6 +47,6 @@
 
 		<h2>Source</h2>
 
-		[link:https://github.com/mrdoob/three.js/blob/master/examples/js/QuickHull.js examples/js/QuickHull.js]
+		[link:https://github.com/mrdoob/three.js/blob/master/examples/js/math/ConvexHull.js examples/js/math/ConvexHull.js]
 	</body>
 </html>

+ 7 - 0
docs/examples/renderers/SVGRenderer.html

@@ -61,6 +61,13 @@
 
 		<h3>[name]()</h3>
 
+		<h2>Properties</h2>
+
+		<h3>[property:Number overdraw]</h3>
+		<p>
+		Number of fractional pixels to enlarge polygons in order to prevent anti-aliasing gaps. Range is [0..1]. Default is *0.5*.
+		</p>
+
 		<h2>Methods</h2>
 
 		<h3>[method:null clear]()</h3>

+ 13 - 12
docs/index.html

@@ -2,7 +2,7 @@
 <html lang="en">
 	<head>
 		<meta charset="utf-8">
-		<title>three.js / documentation</title>
+		<title>three.js docs</title>
 		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
 		<link rel="shortcut icon" href="../files/favicon.ico" />
 		<link rel="stylesheet" type="text/css" href="../files/main.css">
@@ -17,23 +17,23 @@
 
 				<div id="sections">
 					<span class="selected">docs</span>
-					<a href="../examples/">examples</a>
+					<a href="../examples/#webgl_animation_cloth">examples</a>
 				</div>
 
-				<img id="expandButton" src="../files/ic_menu_black_24dp.svg">
+				<div id="expandButton"></div>
 			</div>
 
 			<div id="panelScrim"></div>
 
 			<div id="contentWrapper">
-				<input placeholder="Search" type="text" id="filter" autocorrect="off" autocapitalize="off" spellcheck="false" />
-				<div id="exitSearchButton"></div>
-
-				<select id="language">
-					<option value="en">en</option>
-					<option value="zh">zh</option>
-				</select>
-
+				<div id="inputWrapper">
+					<input placeholder="" type="text" id="filter" autocorrect="off" autocapitalize="off" spellcheck="false" />
+					<div id="exitSearchButton"></div>
+					<select id="language">
+						<option value="en">en</option>
+						<option value="zh">zh</option>
+					</select>
+				</div>
 				<div id="content"></div>
 			</div>
 
@@ -417,7 +417,7 @@
 				if(hash) {
 
 					iframe.src = splitHash[ 0 ] + '.html' + splitHash[ 1 ];
-					subtitle = titles[ splitHash[ 0 ] ] + splitHash[ 1 ] + ' - ';
+					subtitle = titles[ splitHash[ 0 ] ] + splitHash[ 1 ] + '  ';
 
 				} else {
 
@@ -429,6 +429,7 @@
 				document.body.replaceChild( iframe, oldIframe );
 				document.title = subtitle + 'three.js docs';
 
+
 			}
 
 			function decomposePageName( pageName, oldDelimiter, newDelimiter ) {

+ 7 - 6
docs/list.js

@@ -365,6 +365,7 @@ var list = {
 
 			"Loaders": {
 				"BabylonLoader": "examples/loaders/BabylonLoader",
+				"BasisTextureLoader": "examples/loaders/BasisTextureLoader",
 				"DRACOLoader": "examples/loaders/DRACOLoader",
 				"GLTFLoader": "examples/loaders/GLTFLoader",
 				"MMDLoader": "examples/loaders/MMDLoader",
@@ -393,12 +394,12 @@ var list = {
 				"LookupTable": "examples/Lut",
 			},
 
-			"QuickHull": {
-				"Face": "examples/quickhull/Face",
-				"HalfEdge": "examples/quickhull/HalfEdge",
-				"QuickHull": "examples/quickhull/QuickHull",
-				"VertexNode": "examples/quickhull/VertexNode",
-				"VertexList": "examples/quickhull/VertexList"
+			"ConvexHull": {
+				"Face": "examples/math/convexhull/Face",
+				"HalfEdge": "examples/math/convexhull/HalfEdge",
+				"ConvexHull": "examples/math/convexhull/ConvexHull",
+				"VertexNode": "examples/math/convexhull/VertexNode",
+				"VertexList": "examples/math/convexhull/VertexList"
 			},
 
 			"Renderers": {

+ 1 - 1
docs/manual/en/introduction/Creating-a-scene.html

@@ -8,7 +8,7 @@
 		<link type="text/css" rel="stylesheet" href="page.css" />
 	</head>
 	<body>
-		<h1>[name]</h1><br />
+		<h1>[name]</h1>
 
 		<p>The goal of this section is to give a brief introduction to three.js. We will start by setting up a scene, with a spinning cube. A working example is provided at the bottom of the page in case you get stuck and need help.</p>
 

+ 1 - 1
docs/manual/en/introduction/Drawing-lines.html

@@ -12,7 +12,7 @@
 		<div>
 			<p>
 				Let's say you want to draw a line or a circle, not a wireframe [page:Mesh].
-				First we need to setup the [page:WebGLRenderer renderer], [page:Scene scene] and camera (see the Creating a scene page).
+				First we need to set up the [page:WebGLRenderer renderer], [page:Scene scene] and camera (see the Creating a scene page).
 			</p>
 
 			<p>Here is the code that we will use:</p>

+ 0 - 2
docs/manual/en/introduction/How-to-update-things.html

@@ -178,8 +178,6 @@ geometry.tangentsNeedUpdate = true;
 
 			<p>Also GLstate related parameters can change any time (depthTest, blending, polygonOffset, etc).</p>
 
-			<p>Flat / smooth shading is baked into normals. You need to reset normals buffer (see above).</p>
-
 			<p>The following properties can't be easily changed at runtime (once the material is rendered at least once):</p>
 			<ul>
 				<li>numbers and types of uniforms</li>

+ 3 - 1
docs/manual/en/introduction/How-to-use-WebGL2.html

@@ -100,7 +100,7 @@ var material = new THREE.ShaderMaterial( {
 		Have a look at one of the official examples in order to see WebGL 2 features in action.<br /><br />
 
 		[example:webgl2_materials_texture3d WebGL2 / materials / texture3d]<br />
-		[example:webgl2_materials_texture3d_volume WebGL2 / materials / texture3d / volume]<br />
+		[example:webgl2_materials_texture2darray WebGL2 / materials / texture2darray]<br />
 		[example:webgl2_multisampled_renderbuffers WebGL2 / multisampled renderbuffers]
 	</p>
 
@@ -111,7 +111,9 @@ var material = new THREE.ShaderMaterial( {
 		overview about what's already available in the latest version of three.js.
 		<ul>
 			<li>3D Textures</li>
+			<li>2D Texture Arrays</li>
 			<li>Multisampled Renderbuffers</li>
+			<li>Non-power of two (POT) textures work just the same as POT textures now. No resizing is required for best quality.</li>
 		</ul>
 
 	</p>

+ 72 - 0
docs/manual/en/introduction/Import-via-modules.html

@@ -98,11 +98,22 @@
 				</li>
 				<li>curves
 					<ul>
+						<li>CurveExtras</li>
 						<li>NURBSCurve</li>
 						<li>NURBSSurface</li>
 						<li>NURBSUtils</li>
 					</ul>
 				</li>
+				<li>effects
+					<ul>
+						<li>AnaglyphEffect</li>
+						<li>AsciiEffect</li>
+						<li>OutlineEffect</li>
+						<li>ParallaxBarrierEffect</li>
+						<li>PeppersGhostEffect</li>
+						<li>StereoEffect</li>
+					</ul>
+				</li>
 				<li>exporters
 					<ul>
 						<li>ColladaExporter</li>
@@ -114,6 +125,32 @@
 						<li>TypedGeometryExporter</li>
 					</ul>
 				</li>
+				<li>geometries
+					<ul>
+						<li>BoxLineGeometry</li>
+						<li>ConvexGeometry</li>
+						<li>DecalGeometry</li>
+						<li>ParametricGeometries</li>
+						<li>TeapotBufferGeometry</li>
+					</ul>
+				</li>
+				<li>interactive
+					<ul>
+						<li>SelectionBox</li>
+						<li>SelectionHelper</li>
+					</ul>
+				</li>
+				<li>lines
+					<ul>
+						<li>Line2</li>
+						<li>LineGeometry</li>
+						<li>LineMaterial</li>
+						<li>LineSegments2</li>
+						<li>LineSegmentsGeometry</li>
+						<li>Wireframe</li>
+						<li>WireframeGeometry2</li>
+					</ul>
+				</li>
 				<li>loaders
 					<ul>
 						<li>3MFLoader</li>
@@ -128,26 +165,54 @@
 						<li>FBXLoader</li>
 						<li>GCodeLoader</li>
 						<li>GLTFLoader</li>
+						<li>HDRCubeTextureLoader</li>
 						<li>KMZLoader</li>
 						<li>KTXLoader</li>
+						<li>LWOLoader</li>
 						<li>MTLLoader</li>
 						<li>OBJLoader</li>
 						<li>PCDLoader</li>
 						<li>PDBLoader</li>
 						<li>PlayCanvasLoader</li>
 						<li>PLYLoader</li>
+						<li>PRWMLoader</li>
+						<li>PVRLoader</li>
 						<li>RGBELoader</li>
 						<li>STLLoader</li>
 						<li>SVGLoader</li>
+						<li>TDSLoader</li>
 						<li>TGALoader</li>
 						<li>VRMLLoader</li>
 					</ul>
 				</li>
+				<li>math
+					<ul>
+						<li>ColorConverter</li>
+						<li>ConvexHull</li>
+						<li>ImprovedNoise</li>
+						<li>Lut</li>
+						<li>SimplexNoise</li>
+					</ul>
+				</li>
+				<li>modifiers
+					<ul>
+						<li>ExplodeModifier</li>
+						<li>SimplifyModifier</li>
+						<li>SubdivisionModifier</li>
+						<li>TessellateModifier</li>
+					</ul>
+				</li>
 				<li>objects
 					<ul>
+						<li>Fire</li>
 						<li>Lensflare</li>
 						<li>Reflector</li>
 						<li>Refractor</li>
+						<li>ReflectorRTT</li>
+						<li>ShadowMesh</li>
+						<li>Sky</li>
+						<li>Water</li>
+						<li>Water2</li>
 					</ul>
 				</li>
 				<li>pmrem
@@ -177,6 +242,7 @@
 						<li>ShaderPass</li>
 						<li>SMAAPass</li>
 						<li>SSAARenderPass</li>
+						<li>SSAOPass</li>
 						<li>TAARenderPass</li>
 						<li>TexturePass</li>
 						<li>UnrealBloomPass</li>
@@ -190,6 +256,7 @@
 						<li>SoftwareRenderer</li>
 						<li>SVGRenderer</li>
 						<li>RaytracingRenderer</li>
+						<li>WebGLDeferredRenderer</li>
 					</ul>
 				</li>
 				<li>shaders
@@ -215,6 +282,7 @@
 						<li>FresnelShader</li>
 						<li>FXAAShader</li>
 						<li>GammaCorrectionShader</li>
+						<li>GodRaysShader</li>
 						<li>HalftoneShader</li>
 						<li>HorizontalBlurShader</li>
 						<li>HorizontalTiltShiftShader</li>
@@ -229,11 +297,15 @@
 						<li>RGBShiftShader</li>
 						<li>SAOShader</li>
 						<li>SepiaShader</li>
+						<li>SkinShader</li>
 						<li>SMAAShader</li>
 						<li>SobelOperatorShader</li>
 						<li>SSAOShader</li>
 						<li>TechnicolorShader</li>
+						<li>TerrainShader</li>
 						<li>ToneMapShader</li>
+						<li>ToonShader</li>
+						<li>TranslucentShader</li>
 						<li>TriangleBlurShader</li>
 						<li>UnpackDepthRGBAShader</li>
 						<li>VerticalBlurShader</li>

+ 1 - 4
docs/manual/en/introduction/Useful-links.html

@@ -17,10 +17,7 @@
 
 			Note also that as three.js is under rapid development, a lot of these links will contain information that is
 			out of date - if something isn't working as you'd expect or as one of these links says it should,
-			check the browser console for warnings or errors, the relevant docs pages and especially the [page:DeprecatedList].<br /><br />
-
-			In addition to this page, mrdoob maintains a collection of links related to three.js over on Google+.
-			Check them out <a href="https://plus.google.com/+ThreejsOrg">here</a>.
+			check the browser console for warnings or errors, the relevant docs pages and especially the [page:DeprecatedList].
 		</p>
 
 		<h2>Help forums</h2>

+ 127 - 95
docs/page.css

@@ -1,33 +1,56 @@
 :root {
 	--color-blue: #049EF4;
 	--text-color: #444;
-	--border-style: 1px solid #EEE;
-	--header-height: 56px;
+
+	--font-size: 14px;
+	--line-height: 24px;
+	--font-size-sanserif: 1.14285714286rem;
+	--line-height-sanserif: 1.71428571rem;
+
+	--border-style: 1px solid #E8E8E8;
+	--header-height: 48px;
+	--panel-width: 300px;
+	--panel-padding: 1.143rem;
+	--page-padding: 1.75rem;
+	--max-width: 760px;
+	--icon-size: 1.428rem;
 }
 
 @font-face {
 	font-family: 'Roboto Mono';
-	src: local('Roboto Mono'), url('../files/RobotoMono-Regular.woff2') format('woff2');
-	font-weight: normal;
+	src: local('Roboto Mono'), local('RobotoMono-Regular'), url('../files/RobotoMono-Regular.woff2') format('woff2');
 	font-style: normal;
+	font-weight: 400;
 }
-
 @font-face {
-	font-family: 'SF-Pro-Text';
-	src: local('SF-Pro-Text'), url('../files/SF-Pro-Text-Regular.otf');
-	font-weight: normal;
+	font-family: 'Inter';
 	font-style: normal;
+	font-weight: 400;
+	src: local('Inter-Regular'), url("../files/Inter-Regular.woff2?v=3.6") format("woff2");
+}
+@font-face {
+	font-family: 'Inter';
+	font-style: normal;
+	font-weight: 600;
+	src: local('Inter-SemiBold'), url("../files/Inter-SemiBold.woff2?v=3.6") format("woff2");
+}
+
+html {
+	font-family: 'Inter', sans-serif;
+	font-size: var(--font-size);
+	line-height: var(--line-height);
 }
 
 body {
-	padding: 20px 24px 40px 24px;
-	margin: 0;
 	color: var(--text-color);
-	font-family: 'SF-Pro-Text', sans-serif;
-	font-size: 16px;
-	line-height: 24px;
 	tab-size: 4;
 	overflow: auto;
+	max-width: var(--max-width);
+	margin: 0 auto;
+	padding-top: var(--page-padding);
+	padding-bottom: var(--page-padding);
+	padding-right: var(--page-padding);
+	padding-left: calc(var(--page-padding) + var(--panel-width));
 }
 
 a {
@@ -37,97 +60,106 @@ a {
 }
 
 h1 {
-	color: var(--color-blue);
-	font-size: 2.4em;
+	font-size: 2.8rem;
+	line-height: 3.4rem;
 	font-weight: normal;
-	line-height: 1.36em;
-	margin-top: 16px;
-	margin-bottom: -16px;
-	text-indent: -2px;
+	margin-top: 1rem;
+	margin-bottom: -0.2rem;
+	margin-left: -2px;
 }
 
 h2 {
-	color: var(--color-blue);
-	font-size: 1.8em;
-	line-height: 1.32em;
+	font-size: 2rem;
+	line-height: 2.6rem;
 	font-weight: normal;
-	margin-top: 40px;
-	margin-bottom: 12px;
-	text-indent: -1px;
+	margin-top: 2rem;
+	margin-bottom: -0.4rem;
 }
 
 h3 {
-	font-size: 1.32em;
-	line-height: 1.48em;
+	font-size: 1.42857143rem;
+	line-height: 2.14285714rem;
 	font-weight: normal;
-	text-indent: -1px;
-	margin-top: 24px;
-	margin-bottom: 12px;
+	margin-top: 1.8rem;
+	margin-bottom: -0.4rem;
+}
+
+p,
+div,
+table,
+ol,
+ul {
+	font-size: var(--font-size-sanserif);
+	line-height: var(--line-height-sanserif);
+	margin-top: .8rem;
+	margin-bottom: .8rem;
 }
 
 p {
-	margin-top: 24px;
-	margin-bottom: 24px;
+	padding-right: 1rem;
 }
+
 ul, ol {
 	box-sizing: border-box;
-	padding-left: 20px;
+	padding-left: 24px;
 }
 ul li,
 ol li {
-	padding-left: 4px;
-	margin-bottom: 4px;
+	padding-left: 0.25rem;
+	margin-bottom: 0.25rem;
 }
 
 li ul,
 li ol {
-	margin-top: 4px;
-}
-
-body {
-	max-width: 760px;
-	margin-left: auto;
-	margin-right: auto;
+	margin-top: 0.25rem;
 }
 
-table,
-pre,
 code {
-	margin-left: -24px;
-	margin-right: -24px;
-	margin-top: 20px;
-	margin-bottom: 20px;
+	margin: 1.2rem calc(-1 * var(--page-padding));
 }
 
-div {
-	margin-bottom: 20px;
+ol code,
+ul code {
+	margin: 1.2rem 0;
 }
 
-.desc {
-	padding-left: 0px;
+code.inline {
+	display: inline-block;
+	vertical-align: middle;
+	border-radius: 4px;
+	padding: 0px 5px;
+	background: #F5F5F5;
+	margin: 0;
 }
 
-br {
-	display: none;
+table {
+	width: 100%;
+	border-collapse: collapse;
 }
 
-table {
-	border-spacing: 24px 4px;
+.desc {
+	padding-left: 0px;
 }
-table,
-table tr,
+
+table th,
 table td {
 	text-align: left;
+	vertical-align: top;
+	padding: .6rem .4rem;
+	border-bottom: var(--border-style);
 }
 
 table th {
 	text-decoration: none;
-	padding: 2px 0;
+}
+table th:first-child,
+table td:first-child {
+	padding-left: 0;
 }
 
-code {
+code:not(.inline) {
 	display: block;
-	padding: 20px 24px;
+	padding: 1.25rem var(--page-padding);
 	white-space: pre-wrap;
 	overflow: auto;
 	box-sizing: border-box;
@@ -139,52 +171,46 @@ iframe {
 	border:0;
 }
 
-th {
-	padding: 10px;
-	text-decoration: underline;
-}
-
-td {
-	text-align: center;
-}
-
 table code {
-	padding: 2px;
+	padding: 0px;
 	margin: 0px;
 	width: auto;
 }
 
 strong {
-	color: #000;
-	font-weight: normal;
+	font-weight: 600;
 }
 
+
+/* TODO: Duplicate styles in main.css. Needed here cause button is inside the iframe */
 #button {
 	position: fixed;
-	bottom: 12px;
-	right: 12px;
+	bottom: 1rem;
+	right: 1rem;
 
-	padding: 8px;
-	border-radius: 50%;
+	padding: 0.75rem;
+	border-radius: 2rem;
 	margin-bottom: 0px;
 
-	background-color: #E5E5E5;
+	background-color: #FFF;
 	opacity: .9;
-}
+	z-index: 999;
 
+	box-shadow: 0 0 4px rgba(0,0,0,.15);
+}
 	#button:hover {
 		cursor: pointer;
 		opacity: 1;
 	}
-
 	#button img {
 		display: block;
-		width: 20px;
+		width: calc(1.125 * var(--icon-size));
 	}
 
 a.permalink {
 	float: right;
 	margin-left: 5px;
+	display: none;
 }
 
 a.param,
@@ -193,35 +219,41 @@ span.param {
 }
 
 a.param:hover {
-	color: #777;
+	color: var(--text-color);
 }
 
-sup, sub {
-	/* prevent superscript and subscript elements from affecting line-height */
-	vertical-align: baseline;
-	position: relative;
-	top: -0.4em;
-}
 
-sub {
-	top: 0.4em;
+@media all and ( min-width: 1700px ) {
+	:root {
+		--panel-width: 360px;
+		--font-size: 18px;
+		--line-height: 28px;
+		--header-height: 56px;
+		--max-width: 1160px;
+	}
 }
 
 /* mobile */
 
 @media all and ( max-width: 640px ) {
+	:root {
+		--page-padding: var(--panel-padding);
+	}
+
 	body {
-		padding: 16px 20px;
+		padding: var(--page-padding);
 	}
 
 	h1 {
+		font-size: 2rem;
+		line-height: 2.6rem;
+		padding-right: 2rem;
 		margin-top: 0;
-		font-size: 26px;
 	}
 
 	h2 {
-		margin-top: 20px;
-		font-size: 18px;
-		line-height: 25px;
+		font-size: 1.6rem;
+		line-height: 2.2rem;
+		margin-top: 1.6rem;
 	}
 }

+ 0 - 2
docs/prettify/threejs.css

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

+ 1 - 1
docs/scenes/geometry-browser.html

@@ -24,7 +24,7 @@
 		<script src="../../build/three.min.js"></script>
 		<script src='../../examples/js/libs/dat.gui.min.js'></script>
 		<script src="../../examples/js/controls/OrbitControls.js"></script>
-		<script src="../../examples/js/ParametricGeometries.js"></script>
+		<script src="../../examples/js/geometries/ParametricGeometries.js"></script>
 
 		<script src='js/geometry.js'></script>
 

+ 9 - 1
editor/sw.js

@@ -1,8 +1,10 @@
-// r104
+// r105
 
 const staticAssets = [
 	'./',
 
+	'../files/favicon.ico',
+
 	'../build/three.js',
 	'../examples/js/libs/system.min.js',
 
@@ -125,9 +127,14 @@ const staticAssets = [
 	'./js/Sidebar.Geometry.BoxGeometry.js',
 	'./js/Sidebar.Geometry.CircleGeometry.js',
 	'./js/Sidebar.Geometry.CylinderGeometry.js',
+	'./js/Sidebar.Geometry.ExtrudeGeometry.js',
 	'./js/Sidebar.Geometry.IcosahedronGeometry.js',
+	'./js/Sidebar.Geometry.OctahedronGeometry.js',
 	'./js/Sidebar.Geometry.PlaneGeometry.js',
+	'./js/Sidebar.Geometry.RingGeometry.js',
 	'./js/Sidebar.Geometry.SphereGeometry.js',
+	'./js/Sidebar.Geometry.ShapeGeometry.js',
+	'./js/Sidebar.Geometry.TetrahedronGeometry.js',
 	'./js/Sidebar.Geometry.TorusGeometry.js',
 	'./js/Sidebar.Geometry.TorusKnotGeometry.js',
 	'./js/Sidebar.Geometry.TubeGeometry.js',
@@ -141,6 +148,7 @@ const staticAssets = [
 	'./js/Strings.js',
 	'./js/Toolbar.js',
 	'./js/Viewport.js',
+	'./js/Viewport.Camera.js',
 	'./js/Viewport.Info.js',
 
 	'./js/Command.js',

+ 3 - 24
examples/css2d_label.html

@@ -4,39 +4,18 @@
 		<meta charset="utf-8">
 		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
 		<title>three.js css2d - label</title>
+		<link type="text/css" rel="stylesheet" href="main.css">
 		<style>
-			body {
-				background-color: #000;
-				margin: 0;
-				overflow: hidden;
-			}
-			#info {
-				position: absolute;
-				top: 0px;
-				width: 100%;
-				color: #FFF;
-				padding: 5px;
-				font-family: Monospace;
-				font-size: 13px;
-				text-align: center;
-				z-index: 1;
-			}
-
-			.label{
+			.label {
 				color: #FFF;
 				font-family: sans-serif;
 				padding: 2px;
 				background: rgba( 0, 0, 0, .6 );
 			}
-
-			a {
-				color: #000000;
-			}
-
 		</style>
 	</head>
 	<body>
-		<div id="info"><a href="http://threejs.org" target="_blank" rel="noopener">three.js</a> - three.js css2d - label</div>
+		<div id="info"><a href="http://threejs.org" target="_blank" rel="noopener">three.js</a> css2d - label</div>
 
 		<script src="../build/three.js"></script>
 

+ 21 - 45
examples/css3d_molecules.html

@@ -4,71 +4,49 @@
 		<title>three.js css3d - molecules</title>
 		<meta charset="utf-8">
 		<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="main.css">
 		<style>
-			html, body {
-				height: 100%;
-			}
-
 			body {
 				background-color: #050505;
 				background: radial-gradient(ellipse at center,  rgba(43,45,48,1) 0%,rgba(0,0,0,1) 100%);
-
-				margin: 0;
-				font-family: Arial;
-				overflow: hidden;
-			}
-
-			a {
-				color: #ffffff;
 			}
 
-			#info {
+			#topmenu {
 				position: absolute;
-				top: 0px;
+				top: 50px;
 				width: 100%;
-				color: #ffffff;
-				padding: 5px;
-				font-family: Monospace;
-				font-size: 13px;
-				font-weight: bold;
+				padding: 10px;
+				box-sizing: border-box;
 				text-align: center;
-				z-index: 1;
 			}
 
 			#menu {
 				position: absolute;
 				bottom: 20px;
 				width: 100%;
+				padding: 10px;
+				box-sizing: border-box;
 				text-align: center;
-				padding: 0;
-				margin: 0;
-			}
-
-			#topmenu {
-				position: absolute;
-				top: 10px;
-				right: 10px;
-				width: 100%;
-				text-align: right;
-				padding: 0;
-				margin: 0;
-				z-index: 1;
 			}
 
 			button {
 				color: rgb(255,255,255);
-				background: transparent;
+				background: rgb(255,255,255,0.1);
 				border: 0px;
 				padding: 5px 10px;
+				margin: 2px;
+				font-size: 14px;
 				cursor: pointer;
 			}
-			button:hover {
-				background-color: rgba(0,255,255,0.5);
-			}
-			button:active {
-				color: #000000;
-				background-color: rgba(0,255,255,1);
-			}
+
+				button:hover {
+					background-color: rgba(0,255,255,0.5);
+				}
+
+				button:active {
+					color: #000000;
+					background-color: rgba(0,255,255,1);
+				}
 
 			.bond {
 				width: 5px;
@@ -85,11 +63,9 @@
 		<script src="js/loaders/PDBLoader.js"></script>
 
 		<div id="container"></div>
-		<div id="info"><a href="http://threejs.org" target="_blank" rel="noopener">three.js css3d</a> - molecules</div>
+		<div id="info"><a href="http://threejs.org" target="_blank" rel="noopener">three.js</a> css3d - molecules</div>
 		<div id="topmenu">
-			<button id="b_a">Atoms</button>
-			<button id="b_b">Bonds</button>
-			<button id="b_ab">Atoms + Bonds</button>
+			<button id="b_a">Atoms</button><button id="b_b">Bonds</button><button id="b_ab">Atoms + Bonds</button>
 		</div>
 		<div id="menu"></div>
 

+ 6 - 17
examples/css3d_orthographic.html

@@ -3,32 +3,21 @@
 	<head>
 		<meta charset="utf-8">
 		<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="main.css">
 		<style>
 			body {
-				background-color: #ffffff;
-				margin: 0;
-				overflow: hidden;
+				background-color: #f0f0f0;
 			}
-			#info {
-				position: absolute;
-				top: 0px;
-				width: 100%;
-				color: #000000;
-				padding: 5px;
-				font-family: Monospace;
-				font-size: 13px;
-				text-align: center;
-				z-index: 1;
-			}
-
 			a {
+				color: #f00;
+			}
+			#info {
 				color: #000000;
 			}
-
 		</style>
 	</head>
 	<body>
-		<div id="info"><a href="http://threejs.org" target="_blank" rel="noopener">three.js</a> - css3d orthographic</div>
+		<div id="info"><a href="http://threejs.org" target="_blank" rel="noopener">three.js</a> css3d - orthographic</div>
 
 		<script src="../build/three.js"></script>
 

+ 1 - 24
examples/css3d_panorama.html

@@ -4,30 +4,7 @@
 		<title>three.js css3d - panorama</title>
 		<meta charset="utf-8">
 		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
-		<style>
-			body {
-				background-color: #000000;
-				margin: 0;
-				cursor: move;
-				overflow: hidden;
-			}
-
-			a {
-				color: #ffffff;
-			}
-
-			#info {
-				position: absolute;
-				width: 100%;
-				color: #ffffff;
-				padding: 5px;
-				font-family: Monospace;
-				font-size: 13px;
-				font-weight: bold;
-				text-align: center;
-				z-index: 1;
-			}
-		</style>
+		<link type="text/css" rel="stylesheet" href="main.css">
 	</head>
 	<body>
 		<script src="../build/three.js"></script>

+ 2 - 25
examples/css3d_panorama_deviceorientation.html

@@ -4,37 +4,14 @@
 		<title>three.js css3d - panorama - device orientation</title>
 		<meta charset="utf-8">
 		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
-		<style>
-			body {
-				background-color: #000000;
-				margin: 0;
-				cursor: move;
-				overflow: hidden;
-			}
-
-			a {
-				color: #ffffff;
-			}
-
-			#info {
-				position: absolute;
-				width: 100%;
-				color: #ffffff;
-				padding: 5px;
-				font-family: Monospace;
-				font-size: 13px;
-				font-weight: bold;
-				text-align: center;
-				z-index: 1;
-			}
-		</style>
+		<link type="text/css" rel="stylesheet" href="main.css">
 	</head>
 	<body>
 		<script src="../build/three.js"></script>
 		<script src="js/controls/DeviceOrientationControls.js"></script>
 		<script src="js/renderers/CSS3DRenderer.js"></script>
 
-		<div id="info"><a href="http://threejs.org" target="_blank" rel="noopener">three.js css3d</a> - panorama - device orientation. cubemap by <a href="http://www.humus.name/index.php?page=Textures" target="_blank" rel="noopener">Humus</a>.</div>
+		<div id="info"><a href="http://threejs.org" target="_blank" rel="noopener">three.js</a> css3d - panorama - device orientation.<br/>cubemap by <a href="http://www.humus.name/index.php?page=Textures" target="_blank" rel="noopener">Humus</a>.</div>
 
 		<script>
 

+ 7 - 26
examples/css3d_periodictable.html

@@ -4,32 +4,10 @@
 		<title>three.js css3d - periodic table</title>
 		<meta charset="utf-8">
 		<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="main.css">
 		<style>
-			html, body {
-				height: 100%;
-			}
-
-			body {
-				background-color: #000000;
-				margin: 0;
-				font-family: Helvetica, sans-serif;;
-				overflow: hidden;
-			}
-
 			a {
-				color: #ffffff;
-			}
-
-			#info {
-				position: absolute;
-				width: 100%;
-				color: #ffffff;
-				padding: 5px;
-				font-family: Monospace;
-				font-size: 13px;
-				font-weight: bold;
-				text-align: center;
-				z-index: 1;
+				color: #8ff;
 			}
 
 			#menu {
@@ -44,7 +22,9 @@
 				height: 160px;
 				box-shadow: 0px 0px 12px rgba(0,255,255,0.5);
 				border: 1px solid rgba(127,255,255,0.25);
+				font-family: Helvetica, sans-serif;
 				text-align: center;
+				line-height: normal;
 				cursor: default;
 			}
 
@@ -89,9 +69,11 @@
 				padding: 5px 10px;
 				cursor: pointer;
 			}
+
 			button:hover {
 				background-color: rgba(0,255,255,0.5);
 			}
+
 			button:active {
 				color: #000000;
 				background-color: rgba(0,255,255,0.75);
@@ -104,7 +86,7 @@
 		<script src="js/controls/TrackballControls.js"></script>
 		<script src="js/renderers/CSS3DRenderer.js"></script>
 
-		<div id="info"><a href="http://threejs.org" target="_blank" rel="noopener">three.js css3d</a> - periodic table. <a href="https://plus.google.com/113862800338869870683/posts/QcFk5HrWran" target="_blank" rel="noopener">info</a>.</div>
+		<div id="info"><a href="http://threejs.org" target="_blank" rel="noopener">three.js</a> css3d - periodic table. <a href="https://plus.google.com/113862800338869870683/posts/QcFk5HrWran" target="_blank" rel="noopener">info</a>.</div>
 		<div id="container"></div>
 		<div id="menu">
 			<button id="table">TABLE</button>
@@ -360,7 +342,6 @@
 				//
 
 				controls = new THREE.TrackballControls( camera, renderer.domElement );
-				controls.rotateSpeed = 0.5;
 				controls.minDistance = 500;
 				controls.maxDistance = 6000;
 				controls.addEventListener( 'change', render );

+ 4 - 20
examples/css3d_sandbox.html

@@ -3,37 +3,21 @@
 	<head>
 		<meta charset="utf-8">
 		<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="main.css">
 		<style>
 			body {
-				background-color: #ffffff;
-				margin: 0;
-				overflow: hidden;
+				color: #000;
 			}
-			#info {
-				position: absolute;
-				top: 0px;
-				width: 100%;
-				color: #000000;
-				padding: 5px;
-				font-family: Monospace;
-				font-size: 13px;
-				text-align: center;
-				z-index: 1;
-			}
-
 			a {
-				color: #000000;
+				color: #080;
 			}
-
 		</style>
 	</head>
 	<body>
-		<div id="info"><a href="http://threejs.org" target="_blank" rel="noopener">three.js</a> - css3d sandbox</div>
+		<div id="info"><a href="http://threejs.org" target="_blank" rel="noopener">three.js</a> css3d - sandbox</div>
 
 		<script src="../build/three.js"></script>
-
 		<script src="js/controls/TrackballControls.js"></script>
-
 		<script src="js/renderers/CSS3DRenderer.js"></script>
 
 		<script>

+ 5 - 24
examples/css3d_sprites.html

@@ -4,32 +4,14 @@
 		<title>three.js css3d - sprites</title>
 		<meta charset="utf-8">
 		<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="main.css">
 		<style>
-			html, body {
-				height: 100%;
-			}
-
 			body {
-				background-color: #ffffff;
-				margin: 0;
-				font-family: Arial;
-				overflow: hidden;
+				background-color: #fff;
+				color: #000;
 			}
-
 			a {
-				color: #8888ff;
-			}
-
-			#info {
-				position: absolute;
-				width: 100%;
-				color: #000000;
-				padding: 5px;
-				font-family: Monospace;
-				font-size: 13px;
-				font-weight: bold;
-				text-align: center;
-				z-index: 1;
+				color: #48f;
 			}
 		</style>
 	</head>
@@ -39,7 +21,7 @@
 		<script src="js/controls/TrackballControls.js"></script>
 		<script src="js/renderers/CSS3DRenderer.js"></script>
 
-		<div id="info"><a href="http://threejs.org" target="_blank" rel="noopener">three.js css3d</a> - sprites.</div>
+		<div id="info"><a href="http://threejs.org" target="_blank" rel="noopener">three.js</a> css3d - sprites</div>
 		<div id="container"></div>
 
 		<script>
@@ -155,7 +137,6 @@
 				//
 
 				controls = new THREE.TrackballControls( camera, renderer.domElement );
-				controls.rotateSpeed = 0.5;
 
 				//
 

+ 10 - 9
examples/css3d_youtube.html

@@ -4,17 +4,18 @@
 		<title>three.js css3d - youtube</title>
 		<meta charset="utf-8">
 		<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="main.css">
 		<style>
-			html, body {
-				height: 100%;
-				overflow: hidden;
+			body {
+				background-color: #ffffff;
 			}
+
 			#blocker {
 				position: absolute;
-				top: 0px;
-				left: 0px;
-				width: 100%;
-				height: 100%;
+				top: 0;
+				left: 0;
+				bottom: 0;
+				right: 0;
 			}
 		</style>
 	</head>
@@ -86,12 +87,12 @@
 				var blocker = document.getElementById( 'blocker' );
 				blocker.style.display = 'none';
 
-				document.addEventListener( 'mousedown', function () {
+				controls.addEventListener( 'start', function () {
 
 					blocker.style.display = '';
 
 				} );
-				document.addEventListener( 'mouseup', function () {
+				controls.addEventListener( 'end', function () {
 
 					blocker.style.display = 'none';
 

+ 1 - 0
examples/files.js

@@ -126,6 +126,7 @@ var files = {
 		"webgl_loader_sea3d_sound",
 		"webgl_loader_stl",
 		"webgl_loader_svg",
+		"webgl_loader_texture_basis",
 		"webgl_loader_texture_dds",
 		"webgl_loader_texture_exr",
 		"webgl_loader_texture_hdr",

+ 9 - 28
examples/index.html

@@ -2,7 +2,7 @@
 <html lang="en">
 	<head>
 		<meta charset="utf-8">
-		<title>three.js / examples</title>
+		<title>three.js examples</title>
 		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
 		<link rel="shortcut icon" href="../files/favicon.ico" />
 		<link rel="stylesheet" type="text/css" href="../files/main.css">
@@ -10,28 +10,6 @@
 			#panel #content .link {
 				display: block;
 			}
-			#button {
-				position: fixed;
-				bottom: 12px;
-				right: 12px;
-
-				padding: 8px;
-				border-radius: 50%;
-				margin-bottom: 0px; /* TODO div sets it to 20px */
-
-				background-color: #E5E5E5;
-				opacity: .9;
-			}
-
-			#button:hover {
-				cursor: pointer;
-				opacity: 1;
-			}
-
-			#button img {
-				display: block;
-				width: 20px;
-			}
 		</style>
 	</head>
 	<body>
@@ -42,18 +20,21 @@
 				<h1><a href="http://threejs.org">three.js</a></h1>
 
 				<div id="sections">
-					<a href="../docs/">docs</a>
+					<a href="../docs/index.html#manual/introduction/Creating-a-scene">docs</a>
 					<span class="selected">examples</span>
 				</div>
 
-				<img id="expandButton" src="../files/ic_menu_black_24dp.svg">
+				<div id="expandButton"></div>
 			</div>
 
 			<div id="panelScrim"></div>
 
 			<div id="contentWrapper">
-				<input placeholder="Search" type="text" id="filter" autocorrect="off" autocapitalize="off" spellcheck="false" />
-				<div id="exitSearchButton"></div>
+
+				<div id="inputWrapper">
+					<input placeholder="" type="text" id="filter" autocorrect="off" autocapitalize="off" spellcheck="false" />
+					<div id="exitSearchButton"></div>
+				</div>
 
 				<div id="content"></div>
 			</div>
@@ -275,7 +256,7 @@
 
 			var name = file.split( '_' );
 			name.shift();
-			return name.join( ' / ' );
+			return name.join( '<span class="spacer">/</span>' );
 
 		}
 

+ 0 - 71
examples/js/ImprovedNoise.js

@@ -1,71 +0,0 @@
-// http://mrl.nyu.edu/~perlin/noise/
-
-var ImprovedNoise = function () {
-
-	var p = [ 151,160,137,91,90,15,131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,
-		 23,190,6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33,88,237,149,56,87,
-		 174,20,125,136,171,168,68,175,74,165,71,134,139,48,27,166,77,146,158,231,83,111,229,122,60,211,
-		 133,230,220,105,92,41,55,46,245,40,244,102,143,54,65,25,63,161,1,216,80,73,209,76,132,187,208,
-		 89,18,169,200,196,135,130,116,188,159,86,164,100,109,198,173,186,3,64,52,217,226,250,124,123,5,
-		 202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42,223,183,170,213,119,
-		 248,152,2,44,154,163,70,221,153,101,155,167,43,172,9,129,22,39,253,19,98,108,110,79,113,224,232,
-		 178,185,112,104,218,246,97,228,251,34,242,193,238,210,144,12,191,179,162,241,81,51,145,235,249,
-		 14,239,107,49,192,214,31,181,199,106,157,184,84,204,176,115,121,50,45,127,4,150,254,138,236,205,
-		 93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180 ];
-
-	for (var i = 0; i < 256 ; i ++) {
-
-		p[256 + i] = p[i];
-
-	}
-
-	function fade(t) {
-
-		return t * t * t * (t * (t * 6 - 15) + 10);
-
-	}
-
-	function lerp(t, a, b) {
-
-		return a + t * (b - a);
-
-	}
-
-	function grad(hash, x, y, z) {
-
-		var h = hash & 15;
-		var u = h < 8 ? x : y, v = h < 4 ? y : h == 12 || h == 14 ? x : z;
-		return ((h&1) == 0 ? u : -u) + ((h&2) == 0 ? v : -v);
-
-	}
-
-	return {
-
-		noise: function (x, y, z) {
-
-			var floorX = Math.floor(x), floorY = Math.floor(y), floorZ = Math.floor(z);
-
-			var X = floorX & 255, Y = floorY & 255, Z = floorZ & 255;
-
-			x -= floorX;
-			y -= floorY;
-			z -= floorZ;
-
-			var xMinus1 = x - 1, yMinus1 = y - 1, zMinus1 = z - 1;
-
-			var u = fade(x), v = fade(y), w = fade(z);
-
-			var A = p[X] + Y, AA = p[A] + Z, AB = p[A + 1] + Z, B = p[X + 1] + Y, BA = p[B] + Z, BB = p[B + 1] + Z;
-
-			return lerp(w, lerp(v, lerp(u, grad(p[AA], x, y, z),
-							grad(p[BA], xMinus1, y, z)),
-						lerp(u, grad(p[AB], x, yMinus1, z),
-							grad(p[BB], xMinus1, yMinus1, z))),
-					lerp(v, lerp(u, grad(p[AA + 1], x, y, zMinus1),
-							grad(p[BA + 1], xMinus1, y, z - 1)),
-						lerp(u, grad(p[AB + 1], x, yMinus1, zMinus1),
-							grad(p[BB + 1], xMinus1, yMinus1, zMinus1))));
-
-		}
-	}
-};

+ 0 - 23
examples/js/PRNG.js

@@ -1,23 +0,0 @@
-// Park-Miller-Carta Pseudo-Random Number Generator
-// https://github.com/pnitsch/BitmapData.js/blob/master/js/BitmapData.js
-
-var PRNG = function () {
-
-	this.seed = 1;
-	this.next = function() {
-
-		return ( this.gen() / 2147483647 );
-
-	};
-	this.nextRange = function( min, max )	{
-
-		return min + ( ( max - min ) * this.next() )
-
-	};
-	this.gen = function() {
-
-		return this.seed = ( this.seed * 16807 ) % 2147483647;
-
-	};
-
-};

+ 0 - 332
examples/js/ShaderGodRays.js

@@ -1,332 +0,0 @@
-/**
- * @author huwb / http://huwbowles.com/
- *
- * God-rays (crepuscular rays)
- *
- * Similar implementation to the one used by Crytek for CryEngine 2 [Sousa2008].
- * Blurs a mask generated from the depth map along radial lines emanating from the light
- * source. The blur repeatedly applies a blur filter of increasing support but constant
- * sample count to produce a blur filter with large support.
- *
- * My implementation performs 3 passes, similar to the implementation from Sousa. I found
- * just 6 samples per pass produced acceptible results. The blur is applied three times,
- * with decreasing filter support. The result is equivalent to a single pass with
- * 6*6*6 = 216 samples.
- *
- * References:
- *
- * Sousa2008 - Crysis Next Gen Effects, GDC2008, http://www.crytek.com/sites/default/files/GDC08_SousaT_CrysisEffects.ppt
- */
-
-THREE.ShaderGodRays = {
-
-	'godrays_depthMask': {
-
-		uniforms: {
-
-			tInput: {
-				value: null
-			}
-
-		},
-
-		vertexShader: [
-
-			"varying vec2 vUv;",
-
-			"void main() {",
-
-			" vUv = uv;",
-			" gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
-
-			"}"
-
-		].join( "\n" ),
-
-		fragmentShader: [
-
-			"varying vec2 vUv;",
-
-			"uniform sampler2D tInput;",
-
-			"void main() {",
-
-			"	gl_FragColor = vec4( 1.0 ) - texture2D( tInput, vUv );",
-
-
-			"}"
-
-		].join( "\n" )
-
-	},
-
-
-	/**
-	 * The god-ray generation shader.
-	 *
-	 * First pass:
-	 *
-	 * The depth map is blurred along radial lines towards the "sun". The
-	 * output is written to a temporary render target (I used a 1/4 sized
-	 * target).
-	 *
-	 * Pass two & three:
-	 *
-	 * The results of the previous pass are re-blurred, each time with a
-	 * decreased distance between samples.
-	 */
-
-	'godrays_generate': {
-
-		uniforms: {
-
-			tInput: {
-				value: null
-			},
-			fStepSize: {
-				value: 1.0
-			},
-			vSunPositionScreenSpace: {
-				value: new THREE.Vector2( 0.5, 0.5 )
-			}
-
-		},
-
-		vertexShader: [
-
-			"varying vec2 vUv;",
-
-			"void main() {",
-
-				"vUv = uv;",
-				"gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
-
-			"}"
-
-		].join( "\n" ),
-
-		fragmentShader: [
-
-			"#define TAPS_PER_PASS 6.0",
-
-			"varying vec2 vUv;",
-
-			"uniform sampler2D tInput;",
-
-			"uniform vec2 vSunPositionScreenSpace;",
-			"uniform float fStepSize;", // filter step size
-
-			"void main() {",
-
-				// delta from current pixel to "sun" position
-
-				"vec2 delta = vSunPositionScreenSpace - vUv;",
-				"float dist = length( delta );",
-
-				// Step vector (uv space)
-
-				"vec2 stepv = fStepSize * delta / dist;",
-
-				// Number of iterations between pixel and sun
-
-				"float iters = dist/fStepSize;",
-
-				"vec2 uv = vUv.xy;",
-				"float col = 0.0;",
-
-				// This breaks ANGLE in Chrome 22
-				//	- see http://code.google.com/p/chromium/issues/detail?id=153105
-
-				/*
-				// Unrolling didnt do much on my hardware (ATI Mobility Radeon 3450),
-				// so i've just left the loop
-
-				"for ( float i = 0.0; i < TAPS_PER_PASS; i += 1.0 ) {",
-
-					// Accumulate samples, making sure we dont walk past the light source.
-
-					// The check for uv.y < 1 would not be necessary with "border" UV wrap
-					// mode, with a black border color. I don't think this is currently
-					// exposed by three.js. As a result there might be artifacts when the
-					// sun is to the left, right or bottom of screen as these cases are
-					// not specifically handled.
-
-					"col += ( i <= iters && uv.y < 1.0 ? texture2D( tInput, uv ).r : 0.0 );",
-					"uv += stepv;",
-
-				"}",
-				*/
-
-				// Unrolling loop manually makes it work in ANGLE
-
-				"if ( 0.0 <= iters && uv.y < 1.0 ) col += texture2D( tInput, uv ).r;",
-				"uv += stepv;",
-
-				"if ( 1.0 <= iters && uv.y < 1.0 ) col += texture2D( tInput, uv ).r;",
-				"uv += stepv;",
-
-				"if ( 2.0 <= iters && uv.y < 1.0 ) col += texture2D( tInput, uv ).r;",
-				"uv += stepv;",
-
-				"if ( 3.0 <= iters && uv.y < 1.0 ) col += texture2D( tInput, uv ).r;",
-				"uv += stepv;",
-
-				"if ( 4.0 <= iters && uv.y < 1.0 ) col += texture2D( tInput, uv ).r;",
-				"uv += stepv;",
-
-				"if ( 5.0 <= iters && uv.y < 1.0 ) col += texture2D( tInput, uv ).r;",
-				"uv += stepv;",
-
-				// Should technically be dividing by 'iters', but 'TAPS_PER_PASS' smooths out
-				// objectionable artifacts, in particular near the sun position. The side
-				// effect is that the result is darker than it should be around the sun, as
-				// TAPS_PER_PASS is greater than the number of samples actually accumulated.
-				// When the result is inverted (in the shader 'godrays_combine', this produces
-				// a slight bright spot at the position of the sun, even when it is occluded.
-
-				"gl_FragColor = vec4( col/TAPS_PER_PASS );",
-				"gl_FragColor.a = 1.0;",
-
-			"}"
-
-		].join( "\n" )
-
-	},
-
-	/**
-	 * Additively applies god rays from texture tGodRays to a background (tColors).
-	 * fGodRayIntensity attenuates the god rays.
-	 */
-
-	'godrays_combine': {
-
-		uniforms: {
-
-			tColors: {
-				value: null
-			},
-
-			tGodRays: {
-				value: null
-			},
-
-			fGodRayIntensity: {
-				value: 0.69
-			},
-
-			vSunPositionScreenSpace: {
-				value: new THREE.Vector2( 0.5, 0.5 )
-			}
-
-		},
-
-		vertexShader: [
-
-			"varying vec2 vUv;",
-
-			"void main() {",
-
-				"vUv = uv;",
-				"gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
-
-			"}"
-
-			].join( "\n" ),
-
-		fragmentShader: [
-
-			"varying vec2 vUv;",
-
-			"uniform sampler2D tColors;",
-			"uniform sampler2D tGodRays;",
-
-			"uniform vec2 vSunPositionScreenSpace;",
-			"uniform float fGodRayIntensity;",
-
-			"void main() {",
-
-				// Since THREE.MeshDepthMaterial renders foreground objects white and background
-				// objects black, the god-rays will be white streaks. Therefore value is inverted
-				// before being combined with tColors
-
-				"gl_FragColor = texture2D( tColors, vUv ) + fGodRayIntensity * vec4( 1.0 - texture2D( tGodRays, vUv ).r );",
-				"gl_FragColor.a = 1.0;",
-
-			"}"
-
-		].join( "\n" )
-
-	},
-
-
-	/**
-	 * A dodgy sun/sky shader. Makes a bright spot at the sun location. Would be
-	 * cheaper/faster/simpler to implement this as a simple sun sprite.
-	 */
-
-	'godrays_fake_sun': {
-
-		uniforms: {
-
-			vSunPositionScreenSpace: {
-				value: new THREE.Vector2( 0.5, 0.5 )
-			},
-
-			fAspect: {
-				value: 1.0
-			},
-
-			sunColor: {
-				value: new THREE.Color( 0xffee00 )
-			},
-
-			bgColor: {
-				value: new THREE.Color( 0x000000 )
-			}
-
-		},
-
-		vertexShader: [
-
-			"varying vec2 vUv;",
-
-			"void main() {",
-
-				"vUv = uv;",
-				"gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
-
-			"}"
-
-		].join( "\n" ),
-
-		fragmentShader: [
-
-			"varying vec2 vUv;",
-
-			"uniform vec2 vSunPositionScreenSpace;",
-			"uniform float fAspect;",
-
-			"uniform vec3 sunColor;",
-			"uniform vec3 bgColor;",
-
-			"void main() {",
-
-				"vec2 diff = vUv - vSunPositionScreenSpace;",
-
-				// Correct for aspect ratio
-
-				"diff.x *= fAspect;",
-
-				"float prop = clamp( length( diff ) / 0.5, 0.0, 1.0 );",
-				"prop = 0.35 * pow( 1.0 - prop, 3.0 );",
-
-				"gl_FragColor.xyz = mix( sunColor, bgColor, 1.0 - prop );",
-				"gl_FragColor.w = 1.0;",
-
-			"}"
-
-		].join( "\n" )
-
-	}
-
-};

+ 0 - 694
examples/js/ShaderSkin.js

@@ -1,694 +0,0 @@
-/**
- * @author alteredq / http://alteredqualia.com/
- *
- */
-
-
-THREE.ShaderSkin = {
-
-	/* ------------------------------------------------------------------------------------------
-	//	Simple skin shader
-	//		- per-pixel Blinn-Phong diffuse term mixed with half-Lambert wrap-around term (per color component)
-	//		- physically based specular term (Kelemen/Szirmay-Kalos specular reflectance)
-	//
-	//		- diffuse map
-	//		- bump map
-	//		- specular map
-	//		- point, directional and hemisphere lights (use with "lights: true" material option)
-	//		- fog (use with "fog: true" material option)
-	//
-	// ------------------------------------------------------------------------------------------ */
-
-	'skinSimple' : {
-
-		uniforms: THREE.UniformsUtils.merge( [
-
-			THREE.UniformsLib[ "fog" ],
-			THREE.UniformsLib[ "lights" ],
-
-			{
-
-				"enableBump": { value: 0 },
-				"enableSpecular": { value: 0 },
-
-				"tDiffuse": { value: null },
-				"tBeckmann": { value: null },
-
-				"diffuse": { value: new THREE.Color( 0xeeeeee ) },
-				"specular": { value: new THREE.Color( 0x111111 ) },
-				"opacity": { value: 1 },
-
-				"uRoughness": { value: 0.15 },
-				"uSpecularBrightness": { value: 0.75 },
-
-				"bumpMap": { value: null },
-				"bumpScale": { value: 1 },
-
-				"specularMap": { value: null },
-
-				"offsetRepeat": { value: new THREE.Vector4( 0, 0, 1, 1 ) },
-
-				"uWrapRGB": { value: new THREE.Vector3( 0.75, 0.375, 0.1875 ) }
-
-			}
-
-		] ),
-
-		fragmentShader: [
-
-			"#define USE_BUMPMAP",
-
-			"uniform bool enableBump;",
-			"uniform bool enableSpecular;",
-
-			"uniform vec3 diffuse;",
-			"uniform vec3 specular;",
-			"uniform float opacity;",
-
-			"uniform float uRoughness;",
-			"uniform float uSpecularBrightness;",
-
-			"uniform vec3 uWrapRGB;",
-
-			"uniform sampler2D tDiffuse;",
-			"uniform sampler2D tBeckmann;",
-
-			"uniform sampler2D specularMap;",
-
-			"varying vec3 vNormal;",
-			"varying vec2 vUv;",
-
-			"varying vec3 vViewPosition;",
-
-			THREE.ShaderChunk[ "common" ],
-			THREE.ShaderChunk[ "bsdfs" ],
-			THREE.ShaderChunk[ "packing" ],
-			THREE.ShaderChunk[ "lights_pars_begin" ],
-			THREE.ShaderChunk[ "fog_pars_fragment" ],
-			THREE.ShaderChunk[ "bumpmap_pars_fragment" ],
-
-			// Fresnel term
-
-			"float fresnelReflectance( vec3 H, vec3 V, float F0 ) {",
-
-				"float base = 1.0 - dot( V, H );",
-				"float exponential = pow( base, 5.0 );",
-
-				"return exponential + F0 * ( 1.0 - exponential );",
-
-			"}",
-
-			// Kelemen/Szirmay-Kalos specular BRDF
-
-			"float KS_Skin_Specular( vec3 N,", 		// Bumped surface normal
-									"vec3 L,", 		// Points to light
-									"vec3 V,", 		// Points to eye
-									"float m,",  	// Roughness
-									"float rho_s", 	// Specular brightness
-									") {",
-
-				"float result = 0.0;",
-				"float ndotl = dot( N, L );",
-
-				"if( ndotl > 0.0 ) {",
-
-					"vec3 h = L + V;", // Unnormalized half-way vector
-					"vec3 H = normalize( h );",
-
-					"float ndoth = dot( N, H );",
-
-					"float PH = pow( 2.0 * texture2D( tBeckmann, vec2( ndoth, m ) ).x, 10.0 );",
-
-					"float F = fresnelReflectance( H, V, 0.028 );",
-					"float frSpec = max( PH * F / dot( h, h ), 0.0 );",
-
-					"result = ndotl * rho_s * frSpec;", // BRDF * dot(N,L) * rho_s
-
-				"}",
-
-				"return result;",
-
-			"}",
-
-			"void main() {",
-
-				"vec3 outgoingLight = vec3( 0.0 );",	// outgoing light does not have an alpha, the surface does
-				"vec4 diffuseColor = vec4( diffuse, opacity );",
-
-				"vec4 colDiffuse = texture2D( tDiffuse, vUv );",
-				"colDiffuse.rgb *= colDiffuse.rgb;",
-
-				"diffuseColor = diffuseColor * colDiffuse;",
-
-				"vec3 normal = normalize( vNormal );",
-				"vec3 viewerDirection = normalize( vViewPosition );",
-
-				"float specularStrength;",
-
-				"if ( enableSpecular ) {",
-
-					"vec4 texelSpecular = texture2D( specularMap, vUv );",
-					"specularStrength = texelSpecular.r;",
-
-				"} else {",
-
-					"specularStrength = 1.0;",
-
-				"}",
-
-				"#ifdef USE_BUMPMAP",
-
-					"if ( enableBump ) normal = perturbNormalArb( -vViewPosition, normal, dHdxy_fwd() );",
-
-				"#endif",
-
-				// point lights
-
-				"vec3 totalSpecularLight = vec3( 0.0 );",
-				"vec3 totalDiffuseLight = vec3( 0.0 );",
-
-				"#if NUM_POINT_LIGHTS > 0",
-
-					"for ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {",
-
-						"vec3 lVector = pointLights[ i ].position + vViewPosition.xyz;",
-
-						"float attenuation = calcLightAttenuation( length( lVector ), pointLights[ i ].distance, pointLights[ i ].decay );",
-
-						"lVector = normalize( lVector );",
-
-						"float pointDiffuseWeightFull = max( dot( normal, lVector ), 0.0 );",
-						"float pointDiffuseWeightHalf = max( 0.5 * dot( normal, lVector ) + 0.5, 0.0 );",
-						"vec3 pointDiffuseWeight = mix( vec3 ( pointDiffuseWeightFull ), vec3( pointDiffuseWeightHalf ), uWrapRGB );",
-
-						"float pointSpecularWeight = KS_Skin_Specular( normal, lVector, viewerDirection, uRoughness, uSpecularBrightness );",
-
-						"totalDiffuseLight += pointLight[ i ].color * ( pointDiffuseWeight * attenuation );",
-						"totalSpecularLight += pointLight[ i ].color * specular * ( pointSpecularWeight * specularStrength * attenuation );",
-
-					"}",
-
-				"#endif",
-
-				// directional lights
-
-				"#if NUM_DIR_LIGHTS > 0",
-
-					"for( int i = 0; i < NUM_DIR_LIGHTS; i++ ) {",
-
-						"vec3 dirVector = directionalLights[ i ].direction;",
-
-						"float dirDiffuseWeightFull = max( dot( normal, dirVector ), 0.0 );",
-						"float dirDiffuseWeightHalf = max( 0.5 * dot( normal, dirVector ) + 0.5, 0.0 );",
-						"vec3 dirDiffuseWeight = mix( vec3 ( dirDiffuseWeightFull ), vec3( dirDiffuseWeightHalf ), uWrapRGB );",
-
-						"float dirSpecularWeight = KS_Skin_Specular( normal, dirVector, viewerDirection, uRoughness, uSpecularBrightness );",
-
-						"totalDiffuseLight += directionalLights[ i ].color * dirDiffuseWeight;",
-						"totalSpecularLight += directionalLights[ i ].color * ( dirSpecularWeight * specularStrength );",
-
-					"}",
-
-				"#endif",
-
-				// hemisphere lights
-
-				"#if NUM_HEMI_LIGHTS > 0",
-
-					"for ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) {",
-
-						"vec3 lVector = hemisphereLightDirection[ i ];",
-
-						"float dotProduct = dot( normal, lVector );",
-						"float hemiDiffuseWeight = 0.5 * dotProduct + 0.5;",
-
-						"totalDiffuseLight += mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeight );",
-
-						// specular (sky light)
-
-						"float hemiSpecularWeight = 0.0;",
-						"hemiSpecularWeight += KS_Skin_Specular( normal, lVector, viewerDirection, uRoughness, uSpecularBrightness );",
-
-						// specular (ground light)
-
-						"vec3 lVectorGround = -lVector;",
-						"hemiSpecularWeight += KS_Skin_Specular( normal, lVectorGround, viewerDirection, uRoughness, uSpecularBrightness );",
-
-						"vec3 hemiSpecularColor = mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeight );",
-
-						"totalSpecularLight += hemiSpecularColor * specular * ( hemiSpecularWeight * specularStrength );",
-
-					"}",
-
-				"#endif",
-
-				"outgoingLight += diffuseColor.xyz * ( totalDiffuseLight + ambientLightColor * diffuse ) + totalSpecularLight;",
-
-				"gl_FragColor = linearToOutputTexel( vec4( outgoingLight, diffuseColor.a ) );",	// TODO, this should be pre-multiplied to allow for bright highlights on very transparent objects
-
-				THREE.ShaderChunk[ "fog_fragment" ],
-
-			"}"
-
-		].join( "\n" ),
-
-		vertexShader: [
-
-			"uniform vec4 offsetRepeat;",
-
-			"varying vec3 vNormal;",
-			"varying vec2 vUv;",
-
-			"varying vec3 vViewPosition;",
-
-			THREE.ShaderChunk[ "common" ],
-			THREE.ShaderChunk[ "lights_pars_begin" ],
-			THREE.ShaderChunk[ "fog_pars_vertex" ],
-
-			"void main() {",
-
-				"vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );",
-				"vec4 worldPosition = modelMatrix * vec4( position, 1.0 );",
-
-				"vViewPosition = -mvPosition.xyz;",
-
-				"vNormal = normalize( normalMatrix * normal );",
-
-				"vUv = uv * offsetRepeat.zw + offsetRepeat.xy;",
-
-				"gl_Position = projectionMatrix * mvPosition;",
-
-				THREE.ShaderChunk[ "fog_vertex" ],
-
-			"}"
-
-		].join( "\n" )
-
-	},
-
-	/* ------------------------------------------------------------------------------------------
-	//	Skin shader
-	//		- Blinn-Phong diffuse term (using normal + diffuse maps)
-	//		- subsurface scattering approximation by four blur layers
-	//		- physically based specular term (Kelemen/Szirmay-Kalos specular reflectance)
-	//
-	//		- point and directional lights (use with "lights: true" material option)
-	//
-	//		- based on Nvidia Advanced Skin Rendering GDC 2007 presentation
-	//		  and GPU Gems 3 Chapter 14. Advanced Techniques for Realistic Real-Time Skin Rendering
-	//
-	//			http://developer.download.nvidia.com/presentations/2007/gdc/Advanced_Skin.pdf
-	//			http://http.developer.nvidia.com/GPUGems3/gpugems3_ch14.html
-	// ------------------------------------------------------------------------------------------ */
-
-	'skin' : {
-
-		uniforms: THREE.UniformsUtils.merge( [
-
-			THREE.UniformsLib[ "fog" ],
-			THREE.UniformsLib[ "lights" ],
-
-			{
-
-				"passID": { value: 0 },
-
-				"tDiffuse"	: { value: null },
-				"tNormal"	: { value: null },
-
-				"tBlur1"	: { value: null },
-				"tBlur2"	: { value: null },
-				"tBlur3"	: { value: null },
-				"tBlur4"	: { value: null },
-
-				"tBeckmann"	: { value: null },
-
-				"uNormalScale": { value: 1.0 },
-
-				"diffuse":  { value: new THREE.Color( 0xeeeeee ) },
-				"specular": { value: new THREE.Color( 0x111111 ) },
-				"opacity": 	  { value: 1 },
-
-				"uRoughness": 	  		{ value: 0.15 },
-				"uSpecularBrightness": 	{ value: 0.75 }
-
-			}
-
-		] ),
-
-		fragmentShader: [
-
-			"uniform vec3 diffuse;",
-			"uniform vec3 specular;",
-			"uniform float opacity;",
-
-			"uniform float uRoughness;",
-			"uniform float uSpecularBrightness;",
-
-			"uniform int passID;",
-
-			"uniform sampler2D tDiffuse;",
-			"uniform sampler2D tNormal;",
-
-			"uniform sampler2D tBlur1;",
-			"uniform sampler2D tBlur2;",
-			"uniform sampler2D tBlur3;",
-			"uniform sampler2D tBlur4;",
-
-			"uniform sampler2D tBeckmann;",
-
-			"uniform float uNormalScale;",
-
-			"varying vec3 vNormal;",
-			"varying vec2 vUv;",
-
-			"varying vec3 vViewPosition;",
-
-			THREE.ShaderChunk[ "common" ],
-			THREE.ShaderChunk[ "lights_pars_begin" ],
-			THREE.ShaderChunk[ "fog_pars_fragment" ],
-
-			"float fresnelReflectance( vec3 H, vec3 V, float F0 ) {",
-
-				"float base = 1.0 - dot( V, H );",
-				"float exponential = pow( base, 5.0 );",
-
-				"return exponential + F0 * ( 1.0 - exponential );",
-
-			"}",
-
-			// Kelemen/Szirmay-Kalos specular BRDF
-
-			"float KS_Skin_Specular( vec3 N,", 		// Bumped surface normal
-									"vec3 L,", 		// Points to light
-									"vec3 V,", 		// Points to eye
-									"float m,",  	// Roughness
-									"float rho_s", 	// Specular brightness
-									") {",
-
-				"float result = 0.0;",
-				"float ndotl = dot( N, L );",
-
-				"if( ndotl > 0.0 ) {",
-
-					"vec3 h = L + V;", // Unnormalized half-way vector
-					"vec3 H = normalize( h );",
-
-					"float ndoth = dot( N, H );",
-
-					"float PH = pow( 2.0 * texture2D( tBeckmann, vec2( ndoth, m ) ).x, 10.0 );",
-					"float F = fresnelReflectance( H, V, 0.028 );",
-					"float frSpec = max( PH * F / dot( h, h ), 0.0 );",
-
-					"result = ndotl * rho_s * frSpec;", // BRDF * dot(N,L) * rho_s
-
-				"}",
-
-				"return result;",
-
-			"}",
-
-			"void main() {",
-
-				"vec3 outgoingLight = vec3( 0.0 );",	// outgoing light does not have an alpha, the surface does
-				"vec4 diffuseColor = vec4( diffuse, opacity );",
-
-				"vec4 mSpecular = vec4( specular, opacity );",
-
-				"vec4 colDiffuse = texture2D( tDiffuse, vUv );",
-				"colDiffuse *= colDiffuse;",
-
-				"diffuseColor *= colDiffuse;",
-
-				// normal mapping
-
-				"vec4 posAndU = vec4( -vViewPosition, vUv.x );",
-				"vec4 posAndU_dx = dFdx( posAndU ),  posAndU_dy = dFdy( posAndU );",
-				"vec3 tangent = posAndU_dx.w * posAndU_dx.xyz + posAndU_dy.w * posAndU_dy.xyz;",
-				"vec3 normal = normalize( vNormal );",
-				"vec3 binormal = normalize( cross( tangent, normal ) );",
-				"tangent = cross( normal, binormal );",	// no normalization required
-				"mat3 tsb = mat3( tangent, binormal, normal );",
-
-				"vec3 normalTex = texture2D( tNormal, vUv ).xyz * 2.0 - 1.0;",
-				"normalTex.xy *= uNormalScale;",
-				"normalTex = normalize( normalTex );",
-
-				"vec3 finalNormal = tsb * normalTex;",
-				"normal = normalize( finalNormal );",
-
-				"vec3 viewerDirection = normalize( vViewPosition );",
-
-				// point lights
-
-				"vec3 totalDiffuseLight = vec3( 0.0 );",
-				"vec3 totalSpecularLight = vec3( 0.0 );",
-
-				"#if NUM_POINT_LIGHTS > 0",
-
-					"for ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {",
-
-						"vec3 pointVector = normalize( pointLights[ i ].direction );",
-						"float attenuation = calcLightAttenuation( length( lVector ), pointLights[ i ].distance, pointLights[ i ].decay );",
-
-						"float pointDiffuseWeight = max( dot( normal, pointVector ), 0.0 );",
-
-						"totalDiffuseLight += pointLightColor[ i ] * ( pointDiffuseWeight * attenuation );",
-
-						"if ( passID == 1 ) {",
-
-							"float pointSpecularWeight = KS_Skin_Specular( normal, pointVector, viewerDirection, uRoughness, uSpecularBrightness );",
-
-							"totalSpecularLight += pointLightColor[ i ] * mSpecular.xyz * ( pointSpecularWeight * attenuation );",
-
-						"}",
-
-					"}",
-
-				"#endif",
-
-				// directional lights
-
-				"#if NUM_DIR_LIGHTS > 0",
-
-					"for( int i = 0; i < NUM_DIR_LIGHTS; i++ ) {",
-
-						"vec3 dirVector = directionalLights[ i ].direction;",
-
-						"float dirDiffuseWeight = max( dot( normal, dirVector ), 0.0 );",
-
-
-						"totalDiffuseLight += directionalLights[ i ].color * dirDiffuseWeight;",
-
-						"if ( passID == 1 ) {",
-
-							"float dirSpecularWeight = KS_Skin_Specular( normal, dirVector, viewerDirection, uRoughness, uSpecularBrightness );",
-
-							"totalSpecularLight += directionalLights[ i ].color * mSpecular.xyz * dirSpecularWeight;",
-
-						"}",
-
-					"}",
-
-				"#endif",
-
-
-				"outgoingLight += diffuseColor.rgb * ( totalDiffuseLight + totalSpecularLight );",
-
-				"if ( passID == 0 ) {",
-
-					"outgoingLight = sqrt( outgoingLight );",
-
-				"} else if ( passID == 1 ) {",
-
-					//"#define VERSION1",
-
-					"#ifdef VERSION1",
-
-						"vec3 nonblurColor = sqrt(outgoingLight );",
-
-					"#else",
-
-						"vec3 nonblurColor = outgoingLight;",
-
-					"#endif",
-
-					"vec3 blur1Color = texture2D( tBlur1, vUv ).xyz;",
-					"vec3 blur2Color = texture2D( tBlur2, vUv ).xyz;",
-					"vec3 blur3Color = texture2D( tBlur3, vUv ).xyz;",
-					"vec3 blur4Color = texture2D( tBlur4, vUv ).xyz;",
-
-
-					//"gl_FragColor = vec4( blur1Color, gl_FragColor.w );",
-
-					//"gl_FragColor = vec4( vec3( 0.22, 0.5, 0.7 ) * nonblurColor + vec3( 0.2, 0.5, 0.3 ) * blur1Color + vec3( 0.58, 0.0, 0.0 ) * blur2Color, gl_FragColor.w );",
-
-					//"gl_FragColor = vec4( vec3( 0.25, 0.6, 0.8 ) * nonblurColor + vec3( 0.15, 0.25, 0.2 ) * blur1Color + vec3( 0.15, 0.15, 0.0 ) * blur2Color + vec3( 0.45, 0.0, 0.0 ) * blur3Color, gl_FragColor.w );",
-
-
-					"outgoingLight = vec3( vec3( 0.22,  0.437, 0.635 ) * nonblurColor + ",
-										 "vec3( 0.101, 0.355, 0.365 ) * blur1Color + ",
-										 "vec3( 0.119, 0.208, 0.0 )   * blur2Color + ",
-										 "vec3( 0.114, 0.0,   0.0 )   * blur3Color + ",
-										 "vec3( 0.444, 0.0,   0.0 )   * blur4Color );",
-
-					"outgoingLight *= sqrt( colDiffuse.xyz );",
-
-					"outgoingLight += ambientLightColor * diffuse * colDiffuse.xyz + totalSpecularLight;",
-
-					"#ifndef VERSION1",
-
-						"outgoingLight = sqrt( outgoingLight );",
-
-					"#endif",
-
-				"}",
-
-				"gl_FragColor = vec4( outgoingLight, diffuseColor.a );",	// TODO, this should be pre-multiplied to allow for bright highlights on very transparent objects
-
-				THREE.ShaderChunk[ "fog_fragment" ],
-
-			"}"
-
-		].join( "\n" ),
-
-		vertexShader: [
-
-			"#ifdef VERTEX_TEXTURES",
-
-				"uniform sampler2D tDisplacement;",
-				"uniform float uDisplacementScale;",
-				"uniform float uDisplacementBias;",
-
-			"#endif",
-
-			"varying vec3 vNormal;",
-			"varying vec2 vUv;",
-
-			"varying vec3 vViewPosition;",
-
-			THREE.ShaderChunk[ "common" ],
-			THREE.ShaderChunk[ "fog_pars_vertex" ],
-
-			"void main() {",
-
-				"vec4 worldPosition = modelMatrix * vec4( position, 1.0 );",
-
-				"vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );",
-
-				"vViewPosition = -mvPosition.xyz;",
-
-				"vNormal = normalize( normalMatrix * normal );",
-
-				"vUv = uv;",
-
-				// displacement mapping
-
-				"#ifdef VERTEX_TEXTURES",
-
-					"vec3 dv = texture2D( tDisplacement, uv ).xyz;",
-					"float df = uDisplacementScale * dv.x + uDisplacementBias;",
-					"vec4 displacedPosition = vec4( vNormal.xyz * df, 0.0 ) + mvPosition;",
-					"gl_Position = projectionMatrix * displacedPosition;",
-
-				"#else",
-
-					"gl_Position = projectionMatrix * mvPosition;",
-
-				"#endif",
-
-				THREE.ShaderChunk[ "fog_vertex" ],
-
-			"}",
-
-
-		].join( "\n" ),
-
-		vertexShaderUV: [
-
-			"varying vec3 vNormal;",
-			"varying vec2 vUv;",
-
-			"varying vec3 vViewPosition;",
-
-			THREE.ShaderChunk[ "common" ],
-
-			"void main() {",
-
-				"vec4 worldPosition = modelMatrix * vec4( position, 1.0 );",
-
-				"vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );",
-
-				"vViewPosition = -mvPosition.xyz;",
-
-				"vNormal = normalize( normalMatrix * normal );",
-
-				"vUv = uv;",
-
-				"gl_Position = vec4( uv.x * 2.0 - 1.0, uv.y * 2.0 - 1.0, 0.0, 1.0 );",
-
-			"}"
-
-		].join( "\n" )
-
-	},
-
-	/* ------------------------------------------------------------------------------------------
-	// Beckmann distribution function
-	//	- to be used in specular term of skin shader
-	//	- render a screen-aligned quad to precompute a 512 x 512 texture
-	//
-	//		- from http://developer.nvidia.com/node/171
-	 ------------------------------------------------------------------------------------------ */
-
-	"beckmann" : {
-
-		uniforms: {},
-
-		vertexShader: [
-
-			"varying vec2 vUv;",
-
-			"void main() {",
-
-				"vUv = uv;",
-				"gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
-
-			"}"
-
-		].join( "\n" ),
-
-		fragmentShader: [
-
-			"varying vec2 vUv;",
-
-			"float PHBeckmann( float ndoth, float m ) {",
-
-				"float alpha = acos( ndoth );",
-				"float ta = tan( alpha );",
-
-				"float val = 1.0 / ( m * m * pow( ndoth, 4.0 ) ) * exp( -( ta * ta ) / ( m * m ) );",
-				"return val;",
-
-			"}",
-
-			"float KSTextureCompute( vec2 tex ) {",
-
-				// Scale the value to fit within [0,1]  invert upon lookup.
-
-				"return 0.5 * pow( PHBeckmann( tex.x, tex.y ), 0.1 );",
-
-			"}",
-
-			"void main() {",
-
-				"float x = KSTextureCompute( vUv );",
-
-				"gl_FragColor = vec4( x, x, x, 1.0 );",
-
-			"}"
-
-		].join( "\n" )
-
-	}
-
-};

+ 0 - 324
examples/js/ShaderTerrain.js

@@ -1,324 +0,0 @@
-/**
- * @author alteredq / http://alteredqualia.com/
- *
- */
-
-THREE.ShaderTerrain = {
-
-	/* -------------------------------------------------------------------------
-	//	Dynamic terrain shader
-	//		- Blinn-Phong
-	//		- height + normal + diffuse1 + diffuse2 + specular + detail maps
-	//		- point, directional and hemisphere lights (use with "lights: true" material option)
-	//		- shadow maps receiving
-	 ------------------------------------------------------------------------- */
-
-	'terrain' : {
-
-		uniforms: THREE.UniformsUtils.merge( [
-
-			THREE.UniformsLib[ "fog" ],
-			THREE.UniformsLib[ "lights" ],
-
-			{
-
-				"enableDiffuse1": { value: 0 },
-				"enableDiffuse2": { value: 0 },
-				"enableSpecular": { value: 0 },
-				"enableReflection": { value: 0 },
-
-				"tDiffuse1": { value: null },
-				"tDiffuse2": { value: null },
-				"tDetail": { value: null },
-				"tNormal": { value: null },
-				"tSpecular": { value: null },
-				"tDisplacement": { value: null },
-
-				"uNormalScale": { value: 1.0 },
-
-				"uDisplacementBias": { value: 0.0 },
-				"uDisplacementScale": { value: 1.0 },
-
-				"diffuse": { value: new THREE.Color( 0xeeeeee ) },
-				"specular": { value: new THREE.Color( 0x111111 ) },
-				"shininess": { value: 30 },
-				"opacity": { value: 1 },
-
-				"uRepeatBase": { value: new THREE.Vector2( 1, 1 ) },
-				"uRepeatOverlay": { value: new THREE.Vector2( 1, 1 ) },
-
-				"uOffset": { value: new THREE.Vector2( 0, 0 ) }
-
-			}
-
-		] ),
-
-		fragmentShader: [
-
-			"uniform vec3 diffuse;",
-			"uniform vec3 specular;",
-			"uniform float shininess;",
-			"uniform float opacity;",
-
-			"uniform bool enableDiffuse1;",
-			"uniform bool enableDiffuse2;",
-			"uniform bool enableSpecular;",
-
-			"uniform sampler2D tDiffuse1;",
-			"uniform sampler2D tDiffuse2;",
-			"uniform sampler2D tDetail;",
-			"uniform sampler2D tNormal;",
-			"uniform sampler2D tSpecular;",
-			"uniform sampler2D tDisplacement;",
-
-			"uniform float uNormalScale;",
-
-			"uniform vec2 uRepeatOverlay;",
-			"uniform vec2 uRepeatBase;",
-
-			"uniform vec2 uOffset;",
-
-			"varying vec3 vTangent;",
-			"varying vec3 vBinormal;",
-			"varying vec3 vNormal;",
-			"varying vec2 vUv;",
-
-			"varying vec3 vViewPosition;",
-
-			THREE.ShaderChunk[ "common" ],
-			THREE.ShaderChunk[ "bsdfs" ],
-			THREE.ShaderChunk[ "lights_pars_begin" ],
-			THREE.ShaderChunk[ "shadowmap_pars_fragment" ],
-			THREE.ShaderChunk[ "fog_pars_fragment" ],
-
-			"float calcLightAttenuation( float lightDistance, float cutoffDistance, float decayExponent ) {",
- 				"if ( decayExponent > 0.0 ) {",
- 					"return pow( saturate( - lightDistance / cutoffDistance + 1.0 ), decayExponent );",
- 				"}",
- 				"return 1.0;",
- 			"}",
-
-			"void main() {",
-
-				"vec3 outgoingLight = vec3( 0.0 );",	// outgoing light does not have an alpha, the surface does
-				"vec4 diffuseColor = vec4( diffuse, opacity );",
-
-				"vec3 specularTex = vec3( 1.0 );",
-
-				"vec2 uvOverlay = uRepeatOverlay * vUv + uOffset;",
-				"vec2 uvBase = uRepeatBase * vUv;",
-
-				"vec3 normalTex = texture2D( tDetail, uvOverlay ).xyz * 2.0 - 1.0;",
-				"normalTex.xy *= uNormalScale;",
-				"normalTex = normalize( normalTex );",
-
-				"if( enableDiffuse1 && enableDiffuse2 ) {",
-
-					"vec4 colDiffuse1 = texture2D( tDiffuse1, uvOverlay );",
-					"vec4 colDiffuse2 = texture2D( tDiffuse2, uvOverlay );",
-
-					"colDiffuse1 = GammaToLinear( colDiffuse1, float( GAMMA_FACTOR ) );",
-					"colDiffuse2 = GammaToLinear( colDiffuse2, float( GAMMA_FACTOR ) );",
-
-					"diffuseColor *= mix ( colDiffuse1, colDiffuse2, 1.0 - texture2D( tDisplacement, uvBase ) );",
-
-				" } else if( enableDiffuse1 ) {",
-
-					"diffuseColor *= texture2D( tDiffuse1, uvOverlay );",
-
-				"} else if( enableDiffuse2 ) {",
-
-					"diffuseColor *= texture2D( tDiffuse2, uvOverlay );",
-
-				"}",
-
-				"if( enableSpecular )",
-					"specularTex = texture2D( tSpecular, uvOverlay ).xyz;",
-
-				"mat3 tsb = mat3( vTangent, vBinormal, vNormal );",
-				"vec3 finalNormal = tsb * normalTex;",
-
-				"vec3 normal = normalize( finalNormal );",
-				"vec3 viewPosition = normalize( vViewPosition );",
-
-				"vec3 totalDiffuseLight = vec3( 0.0 );",
-				"vec3 totalSpecularLight = vec3( 0.0 );",
-
-				// point lights
-
-				"#if NUM_POINT_LIGHTS > 0",
-
-					"for ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {",
-
-						"vec3 lVector = pointLights[ i ].position + vViewPosition.xyz;",
-
-						"float attenuation = calcLightAttenuation( length( lVector ), pointLights[ i ].distance, pointLights[ i ].decay );",
-
-						"lVector = normalize( lVector );",
-
-						"vec3 pointHalfVector = normalize( lVector + viewPosition );",
-
-						"float pointDotNormalHalf = max( dot( normal, pointHalfVector ), 0.0 );",
-						"float pointDiffuseWeight = max( dot( normal, lVector ), 0.0 );",
-
-						"float pointSpecularWeight = specularTex.r * max( pow( pointDotNormalHalf, shininess ), 0.0 );",
-
-						"totalDiffuseLight += attenuation * pointLights[ i ].color * pointDiffuseWeight;",
-						"totalSpecularLight += attenuation * pointLights[ i ].color * specular * pointSpecularWeight * pointDiffuseWeight;",
-
-					"}",
-
-				"#endif",
-
-				// directional lights
-
-				"#if NUM_DIR_LIGHTS > 0",
-
-					"vec3 dirDiffuse = vec3( 0.0 );",
-					"vec3 dirSpecular = vec3( 0.0 );",
-
-					"for( int i = 0; i < NUM_DIR_LIGHTS; i++ ) {",
-
-						"vec3 dirVector = directionalLights[ i ].direction;",
-						"vec3 dirHalfVector = normalize( dirVector + viewPosition );",
-
-						"float dirDotNormalHalf = max( dot( normal, dirHalfVector ), 0.0 );",
-						"float dirDiffuseWeight = max( dot( normal, dirVector ), 0.0 );",
-
-						"float dirSpecularWeight = specularTex.r * max( pow( dirDotNormalHalf, shininess ), 0.0 );",
-
-						"totalDiffuseLight += directionalLights[ i ].color * dirDiffuseWeight;",
-						"totalSpecularLight += directionalLights[ i ].color * specular * dirSpecularWeight * dirDiffuseWeight;",
-
-					"}",
-
-				"#endif",
-
-				// hemisphere lights
-
-				"#if NUM_HEMI_LIGHTS > 0",
-
-					"vec3 hemiDiffuse  = vec3( 0.0 );",
-					"vec3 hemiSpecular = vec3( 0.0 );",
-
-					"for( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) {",
-
-						"vec3 lVector = hemisphereLightDirection[ i ];",
-
-						// diffuse
-
-						"float dotProduct = dot( normal, lVector );",
-						"float hemiDiffuseWeight = 0.5 * dotProduct + 0.5;",
-
-						"totalDiffuseLight += mix( hemisphereLights[ i ].groundColor, hemisphereLights[ i ].skyColor, hemiDiffuseWeight );",
-
-						// specular (sky light)
-
-						"float hemiSpecularWeight = 0.0;",
-
-						"vec3 hemiHalfVectorSky = normalize( lVector + viewPosition );",
-						"float hemiDotNormalHalfSky = 0.5 * dot( normal, hemiHalfVectorSky ) + 0.5;",
-						"hemiSpecularWeight += specularTex.r * max( pow( hemiDotNormalHalfSky, shininess ), 0.0 );",
-
-						// specular (ground light)
-
-						"vec3 lVectorGround = -lVector;",
-
-						"vec3 hemiHalfVectorGround = normalize( lVectorGround + viewPosition );",
-						"float hemiDotNormalHalfGround = 0.5 * dot( normal, hemiHalfVectorGround ) + 0.5;",
-						"hemiSpecularWeight += specularTex.r * max( pow( hemiDotNormalHalfGround, shininess ), 0.0 );",
-
-						"totalSpecularLight += specular * mix( hemisphereLights[ i ].groundColor, hemisphereLights[ i ].skyColor, hemiDiffuseWeight ) * hemiSpecularWeight * hemiDiffuseWeight;",
-
-					"}",
-
-				"#endif",
-
-				"outgoingLight += diffuseColor.xyz * ( totalDiffuseLight + ambientLightColor + totalSpecularLight );",
-
-				"gl_FragColor = vec4( outgoingLight, diffuseColor.a );",	// TODO, this should be pre-multiplied to allow for bright highlights on very transparent objects
-
-				THREE.ShaderChunk[ "fog_fragment" ],
-
-			"}"
-
-		].join( "\n" ),
-
-		vertexShader: [
-
-			"attribute vec4 tangent;",
-
-			"uniform vec2 uRepeatBase;",
-
-			"uniform sampler2D tNormal;",
-
-			"#ifdef VERTEX_TEXTURES",
-
-				"uniform sampler2D tDisplacement;",
-				"uniform float uDisplacementScale;",
-				"uniform float uDisplacementBias;",
-
-			"#endif",
-
-			"varying vec3 vTangent;",
-			"varying vec3 vBinormal;",
-			"varying vec3 vNormal;",
-			"varying vec2 vUv;",
-
-			"varying vec3 vViewPosition;",
-
-			THREE.ShaderChunk[ "shadowmap_pars_vertex" ],
-			THREE.ShaderChunk[ "fog_pars_vertex" ],
-
-			"void main() {",
-
-				"vNormal = normalize( normalMatrix * normal );",
-
-				// tangent and binormal vectors
-
-				"vTangent = normalize( normalMatrix * tangent.xyz );",
-
-				"vBinormal = cross( vNormal, vTangent ) * tangent.w;",
-				"vBinormal = normalize( vBinormal );",
-
-				// texture coordinates
-
-				"vUv = uv;",
-
-				"vec2 uvBase = uv * uRepeatBase;",
-
-				// displacement mapping
-
-				"#ifdef VERTEX_TEXTURES",
-
-					"vec3 dv = texture2D( tDisplacement, uvBase ).xyz;",
-					"float df = uDisplacementScale * dv.x + uDisplacementBias;",
-					"vec3 displacedPosition = normal * df + position;",
-
-					"vec4 worldPosition = modelMatrix * vec4( displacedPosition, 1.0 );",
-					"vec4 mvPosition = modelViewMatrix * vec4( displacedPosition, 1.0 );",
-
-				"#else",
-
-					"vec4 worldPosition = modelMatrix * vec4( position, 1.0 );",
-					"vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );",
-
-				"#endif",
-
-				"gl_Position = projectionMatrix * mvPosition;",
-
-				"vViewPosition = -mvPosition.xyz;",
-
-				"vec3 normalTex = texture2D( tNormal, uvBase ).xyz * 2.0 - 1.0;",
-				"vNormal = normalMatrix * normalTex;",
-
-				THREE.ShaderChunk[ "shadowmap_vertex" ],
-				THREE.ShaderChunk[ "fog_vertex" ],
-
-			"}"
-
-		].join( "\n" )
-
-	}
-
-};

+ 0 - 331
examples/js/ShaderToon.js

@@ -1,331 +0,0 @@
-/**
- * @author mrdoob / http://mrdoob.com/
- * @author alteredq / http://alteredqualia.com/
- *
- * ShaderToon currently contains:
- *
- *	toon1
- *	toon2
- *	hatching
- *	dotted
- */
-
-THREE.ShaderToon = {
-
-	'toon1' : {
-
-		uniforms: {
-
-			"uDirLightPos": { value: new THREE.Vector3() },
-			"uDirLightColor": { value: new THREE.Color( 0xeeeeee ) },
-
-			"uAmbientLightColor": { value: new THREE.Color( 0x050505 ) },
-
-			"uBaseColor": { value: new THREE.Color( 0xffffff ) }
-
-		},
-
-		vertexShader: [
-
-			"varying vec3 vNormal;",
-			"varying vec3 vRefract;",
-
-			"void main() {",
-
-				"vec4 worldPosition = modelMatrix * vec4( position, 1.0 );",
-				"vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );",
-				"vec3 worldNormal = normalize ( mat3( modelMatrix[0].xyz, modelMatrix[1].xyz, modelMatrix[2].xyz ) * normal );",
-
-				"vNormal = normalize( normalMatrix * normal );",
-
-				"vec3 I = worldPosition.xyz - cameraPosition;",
-				"vRefract = refract( normalize( I ), worldNormal, 1.02 );",
-
-				"gl_Position = projectionMatrix * mvPosition;",
-
-			"}"
-
-		].join( "\n" ),
-
-		fragmentShader: [
-
-			"uniform vec3 uBaseColor;",
-
-			"uniform vec3 uDirLightPos;",
-			"uniform vec3 uDirLightColor;",
-
-			"uniform vec3 uAmbientLightColor;",
-
-			"varying vec3 vNormal;",
-
-			"varying vec3 vRefract;",
-
-			"void main() {",
-
-				"float directionalLightWeighting = max( dot( normalize( vNormal ), uDirLightPos ), 0.0);",
-				"vec3 lightWeighting = uAmbientLightColor + uDirLightColor * directionalLightWeighting;",
-
-				"float intensity = smoothstep( - 0.5, 1.0, pow( length(lightWeighting), 20.0 ) );",
-				"intensity += length(lightWeighting) * 0.2;",
-
-				"float cameraWeighting = dot( normalize( vNormal ), vRefract );",
-				"intensity += pow( 1.0 - length( cameraWeighting ), 6.0 );",
-				"intensity = intensity * 0.2 + 0.3;",
-
-				"if ( intensity < 0.50 ) {",
-
-					"gl_FragColor = vec4( 2.0 * intensity * uBaseColor, 1.0 );",
-
-				"} else {",
-
-					"gl_FragColor = vec4( 1.0 - 2.0 * ( 1.0 - intensity ) * ( 1.0 - uBaseColor ), 1.0 );",
-
-				"}",
-
-			"}"
-
-		].join( "\n" )
-
-	},
-
-	'toon2' : {
-
-		uniforms: {
-
-			"uDirLightPos": { value: new THREE.Vector3() },
-			"uDirLightColor": { value: new THREE.Color( 0xeeeeee ) },
-
-			"uAmbientLightColor": { value: new THREE.Color( 0x050505 ) },
-
-			"uBaseColor": { value: new THREE.Color( 0xeeeeee ) },
-			"uLineColor1": { value: new THREE.Color( 0x808080 ) },
-			"uLineColor2": { value: new THREE.Color( 0x000000 ) },
-			"uLineColor3": { value: new THREE.Color( 0x000000 ) },
-			"uLineColor4": { value: new THREE.Color( 0x000000 ) }
-
-		},
-
-		vertexShader: [
-
-			"varying vec3 vNormal;",
-
-			"void main() {",
-
-				"gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
-				"vNormal = normalize( normalMatrix * normal );",
-
-			"}"
-
-		].join( "\n" ),
-
-		fragmentShader: [
-
-			"uniform vec3 uBaseColor;",
-			"uniform vec3 uLineColor1;",
-			"uniform vec3 uLineColor2;",
-			"uniform vec3 uLineColor3;",
-			"uniform vec3 uLineColor4;",
-
-			"uniform vec3 uDirLightPos;",
-			"uniform vec3 uDirLightColor;",
-
-			"uniform vec3 uAmbientLightColor;",
-
-			"varying vec3 vNormal;",
-
-			"void main() {",
-
-				"float camera = max( dot( normalize( vNormal ), vec3( 0.0, 0.0, 1.0 ) ), 0.4);",
-				"float light = max( dot( normalize( vNormal ), uDirLightPos ), 0.0);",
-
-				"gl_FragColor = vec4( uBaseColor, 1.0 );",
-
-				"if ( length(uAmbientLightColor + uDirLightColor * light) < 1.00 ) {",
-
-					"gl_FragColor *= vec4( uLineColor1, 1.0 );",
-
-				"}",
-
-				"if ( length(uAmbientLightColor + uDirLightColor * camera) < 0.50 ) {",
-
-					"gl_FragColor *= vec4( uLineColor2, 1.0 );",
-
-				"}",
-
-			"}"
-
-		].join( "\n" )
-
-	},
-
-	'hatching' : {
-
-		uniforms: {
-
-			"uDirLightPos":	{ value: new THREE.Vector3() },
-			"uDirLightColor": { value: new THREE.Color( 0xeeeeee ) },
-
-			"uAmbientLightColor": { value: new THREE.Color( 0x050505 ) },
-
-			"uBaseColor":  { value: new THREE.Color( 0xffffff ) },
-			"uLineColor1": { value: new THREE.Color( 0x000000 ) },
-			"uLineColor2": { value: new THREE.Color( 0x000000 ) },
-			"uLineColor3": { value: new THREE.Color( 0x000000 ) },
-			"uLineColor4": { value: new THREE.Color( 0x000000 ) }
-
-		},
-
-		vertexShader: [
-
-			"varying vec3 vNormal;",
-
-			"void main() {",
-
-				"gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
-				"vNormal = normalize( normalMatrix * normal );",
-
-			"}"
-
-		].join( "\n" ),
-
-		fragmentShader: [
-
-			"uniform vec3 uBaseColor;",
-			"uniform vec3 uLineColor1;",
-			"uniform vec3 uLineColor2;",
-			"uniform vec3 uLineColor3;",
-			"uniform vec3 uLineColor4;",
-
-			"uniform vec3 uDirLightPos;",
-			"uniform vec3 uDirLightColor;",
-
-			"uniform vec3 uAmbientLightColor;",
-
-			"varying vec3 vNormal;",
-
-			"void main() {",
-
-				"float directionalLightWeighting = max( dot( normalize(vNormal), uDirLightPos ), 0.0);",
-				"vec3 lightWeighting = uAmbientLightColor + uDirLightColor * directionalLightWeighting;",
-
-				"gl_FragColor = vec4( uBaseColor, 1.0 );",
-
-				"if ( length(lightWeighting) < 1.00 ) {",
-
-					"if ( mod(gl_FragCoord.x + gl_FragCoord.y, 10.0) == 0.0) {",
-
-						"gl_FragColor = vec4( uLineColor1, 1.0 );",
-
-					"}",
-
-				"}",
-
-				"if ( length(lightWeighting) < 0.75 ) {",
-
-					"if (mod(gl_FragCoord.x - gl_FragCoord.y, 10.0) == 0.0) {",
-
-						"gl_FragColor = vec4( uLineColor2, 1.0 );",
-
-					"}",
-				"}",
-
-				"if ( length(lightWeighting) < 0.50 ) {",
-
-					"if (mod(gl_FragCoord.x + gl_FragCoord.y - 5.0, 10.0) == 0.0) {",
-
-						"gl_FragColor = vec4( uLineColor3, 1.0 );",
-
-					"}",
-				"}",
-
-				"if ( length(lightWeighting) < 0.3465 ) {",
-
-					"if (mod(gl_FragCoord.x - gl_FragCoord.y - 5.0, 10.0) == 0.0) {",
-
-						"gl_FragColor = vec4( uLineColor4, 1.0 );",
-
-					"}",
-				"}",
-
-			"}"
-
-		].join( "\n" )
-
-	},
-
-	'dotted' : {
-
-		uniforms: {
-
-			"uDirLightPos":	{ value: new THREE.Vector3() },
-			"uDirLightColor": { value: new THREE.Color( 0xeeeeee ) },
-
-			"uAmbientLightColor": { value: new THREE.Color( 0x050505 ) },
-
-			"uBaseColor":  { value: new THREE.Color( 0xffffff ) },
-			"uLineColor1": { value: new THREE.Color( 0x000000 ) }
-
-		},
-
-		vertexShader: [
-
-			"varying vec3 vNormal;",
-
-			"void main() {",
-
-				"gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
-				"vNormal = normalize( normalMatrix * normal );",
-
-			"}"
-
-		].join( "\n" ),
-
-		fragmentShader: [
-
-			"uniform vec3 uBaseColor;",
-			"uniform vec3 uLineColor1;",
-			"uniform vec3 uLineColor2;",
-			"uniform vec3 uLineColor3;",
-			"uniform vec3 uLineColor4;",
-
-			"uniform vec3 uDirLightPos;",
-			"uniform vec3 uDirLightColor;",
-
-			"uniform vec3 uAmbientLightColor;",
-
-			"varying vec3 vNormal;",
-
-			"void main() {",
-
-				"float directionalLightWeighting = max( dot( normalize(vNormal), uDirLightPos ), 0.0);",
-				"vec3 lightWeighting = uAmbientLightColor + uDirLightColor * directionalLightWeighting;",
-
-				"gl_FragColor = vec4( uBaseColor, 1.0 );",
-
-				"if ( length(lightWeighting) < 1.00 ) {",
-
-					"if ( ( mod(gl_FragCoord.x, 4.001) + mod(gl_FragCoord.y, 4.0) ) > 6.00 ) {",
-
-						"gl_FragColor = vec4( uLineColor1, 1.0 );",
-
-					"}",
-
-				"}",
-
-				"if ( length(lightWeighting) < 0.50 ) {",
-
-					"if ( ( mod(gl_FragCoord.x + 2.0, 4.001) + mod(gl_FragCoord.y + 2.0, 4.0) ) > 6.00 ) {",
-
-						"gl_FragColor = vec4( uLineColor1, 1.0 );",
-
-					"}",
-
-				"}",
-
-			"}"
-
-		].join( "\n" )
-
-	}
-
-};

+ 0 - 324
examples/js/SimplexNoise.js

@@ -1,324 +0,0 @@
-// Ported from Stefan Gustavson's java implementation
-// http://staffwww.itn.liu.se/~stegu/simplexnoise/simplexnoise.pdf
-// Read Stefan's excellent paper for details on how this code works.
-//
-// Sean McCullough [email protected]
-//
-// Added 4D noise
-// Joshua Koo [email protected] 
-
-/**
- * You can pass in a random number generator object if you like.
- * It is assumed to have a random() method.
- */
-var SimplexNoise = function(r) {
-	if (r == undefined) r = Math;
-	this.grad3 = [[ 1,1,0 ],[ -1,1,0 ],[ 1,-1,0 ],[ -1,-1,0 ], 
-                                 [ 1,0,1 ],[ -1,0,1 ],[ 1,0,-1 ],[ -1,0,-1 ], 
-                                 [ 0,1,1 ],[ 0,-1,1 ],[ 0,1,-1 ],[ 0,-1,-1 ]]; 
-
-	this.grad4 = [[ 0,1,1,1 ], [ 0,1,1,-1 ], [ 0,1,-1,1 ], [ 0,1,-1,-1 ],
-	     [ 0,-1,1,1 ], [ 0,-1,1,-1 ], [ 0,-1,-1,1 ], [ 0,-1,-1,-1 ],
-	     [ 1,0,1,1 ], [ 1,0,1,-1 ], [ 1,0,-1,1 ], [ 1,0,-1,-1 ],
-	     [ -1,0,1,1 ], [ -1,0,1,-1 ], [ -1,0,-1,1 ], [ -1,0,-1,-1 ],
-	     [ 1,1,0,1 ], [ 1,1,0,-1 ], [ 1,-1,0,1 ], [ 1,-1,0,-1 ],
-	     [ -1,1,0,1 ], [ -1,1,0,-1 ], [ -1,-1,0,1 ], [ -1,-1,0,-1 ],
-	     [ 1,1,1,0 ], [ 1,1,-1,0 ], [ 1,-1,1,0 ], [ 1,-1,-1,0 ],
-	     [ -1,1,1,0 ], [ -1,1,-1,0 ], [ -1,-1,1,0 ], [ -1,-1,-1,0 ]];
-
-	this.p = [];
-	for (var i = 0; i < 256; i ++) {
-		this.p[i] = Math.floor(r.random() * 256);
-	}
-  // To remove the need for index wrapping, double the permutation table length 
-	this.perm = []; 
-	for (var i = 0; i < 512; i ++) {
-		this.perm[i] = this.p[i & 255];
-	} 
-
-  // A lookup table to traverse the simplex around a given point in 4D. 
-  // Details can be found where this table is used, in the 4D noise method. 
-	this.simplex = [ 
-    [ 0,1,2,3 ],[ 0,1,3,2 ],[ 0,0,0,0 ],[ 0,2,3,1 ],[ 0,0,0,0 ],[ 0,0,0,0 ],[ 0,0,0,0 ],[ 1,2,3,0 ], 
-    [ 0,2,1,3 ],[ 0,0,0,0 ],[ 0,3,1,2 ],[ 0,3,2,1 ],[ 0,0,0,0 ],[ 0,0,0,0 ],[ 0,0,0,0 ],[ 1,3,2,0 ], 
-    [ 0,0,0,0 ],[ 0,0,0,0 ],[ 0,0,0,0 ],[ 0,0,0,0 ],[ 0,0,0,0 ],[ 0,0,0,0 ],[ 0,0,0,0 ],[ 0,0,0,0 ], 
-    [ 1,2,0,3 ],[ 0,0,0,0 ],[ 1,3,0,2 ],[ 0,0,0,0 ],[ 0,0,0,0 ],[ 0,0,0,0 ],[ 2,3,0,1 ],[ 2,3,1,0 ], 
-    [ 1,0,2,3 ],[ 1,0,3,2 ],[ 0,0,0,0 ],[ 0,0,0,0 ],[ 0,0,0,0 ],[ 2,0,3,1 ],[ 0,0,0,0 ],[ 2,1,3,0 ], 
-    [ 0,0,0,0 ],[ 0,0,0,0 ],[ 0,0,0,0 ],[ 0,0,0,0 ],[ 0,0,0,0 ],[ 0,0,0,0 ],[ 0,0,0,0 ],[ 0,0,0,0 ], 
-    [ 2,0,1,3 ],[ 0,0,0,0 ],[ 0,0,0,0 ],[ 0,0,0,0 ],[ 3,0,1,2 ],[ 3,0,2,1 ],[ 0,0,0,0 ],[ 3,1,2,0 ], 
-    [ 2,1,0,3 ],[ 0,0,0,0 ],[ 0,0,0,0 ],[ 0,0,0,0 ],[ 3,1,0,2 ],[ 0,0,0,0 ],[ 3,2,0,1 ],[ 3,2,1,0 ]]; 
-};
-
-SimplexNoise.prototype.dot = function(g, x, y) { 
-	return g[0] * x + g[1] * y;
-};
-
-SimplexNoise.prototype.dot3 = function(g, x, y, z) {
-	return g[0] * x + g[1] * y + g[2] * z; 
-};
-
-SimplexNoise.prototype.dot4 = function(g, x, y, z, w) {
-	return g[0] * x + g[1] * y + g[2] * z + g[3] * w;
-};
-
-SimplexNoise.prototype.noise = function(xin, yin) { 
-	var n0, n1, n2; // Noise contributions from the three corners 
-  // Skew the input space to determine which simplex cell we're in 
-	var F2 = 0.5 * (Math.sqrt(3.0) - 1.0); 
-	var s = (xin + yin) * F2; // Hairy factor for 2D 
-	var i = Math.floor(xin + s); 
-	var j = Math.floor(yin + s); 
-	var G2 = (3.0 - Math.sqrt(3.0)) / 6.0; 
-	var t = (i + j) * G2; 
-	var X0 = i - t; // Unskew the cell origin back to (x,y) space 
-	var Y0 = j - t; 
-	var x0 = xin - X0; // The x,y distances from the cell origin 
-	var y0 = yin - Y0; 
-  // For the 2D case, the simplex shape is an equilateral triangle. 
-  // Determine which simplex we are in. 
-	var i1, j1; // Offsets for second (middle) corner of simplex in (i,j) coords 
-	if (x0 > y0) {i1 = 1; j1 = 0;} // lower triangle, XY order: (0,0)->(1,0)->(1,1) 
-	else {i1 = 0; j1 = 1;}      // upper triangle, YX order: (0,0)->(0,1)->(1,1) 
-  // A step of (1,0) in (i,j) means a step of (1-c,-c) in (x,y), and 
-  // a step of (0,1) in (i,j) means a step of (-c,1-c) in (x,y), where 
-  // c = (3-sqrt(3))/6 
-	var x1 = x0 - i1 + G2; // Offsets for middle corner in (x,y) unskewed coords 
-	var y1 = y0 - j1 + G2; 
-	var x2 = x0 - 1.0 + 2.0 * G2; // Offsets for last corner in (x,y) unskewed coords 
-	var y2 = y0 - 1.0 + 2.0 * G2; 
-  // Work out the hashed gradient indices of the three simplex corners 
-	var ii = i & 255; 
-	var jj = j & 255; 
-	var gi0 = this.perm[ii + this.perm[jj]] % 12; 
-	var gi1 = this.perm[ii + i1 + this.perm[jj + j1]] % 12; 
-	var gi2 = this.perm[ii + 1 + this.perm[jj + 1]] % 12; 
-  // Calculate the contribution from the three corners 
-	var t0 = 0.5 - x0 * x0 - y0 * y0; 
-	if (t0 < 0) n0 = 0.0; 
-	else { 
-		t0 *= t0; 
-		n0 = t0 * t0 * this.dot(this.grad3[gi0], x0, y0);  // (x,y) of grad3 used for 2D gradient 
-	} 
-	var t1 = 0.5 - x1 * x1 - y1 * y1; 
-	if (t1 < 0) n1 = 0.0; 
-	else { 
-		t1 *= t1; 
-		n1 = t1 * t1 * this.dot(this.grad3[gi1], x1, y1); 
-	}
-	var t2 = 0.5 - x2 * x2 - y2 * y2; 
-	if (t2 < 0) n2 = 0.0; 
-	else { 
-		t2 *= t2; 
-		n2 = t2 * t2 * this.dot(this.grad3[gi2], x2, y2); 
-	} 
-  // Add contributions from each corner to get the final noise value. 
-  // The result is scaled to return values in the interval [-1,1]. 
-	return 70.0 * (n0 + n1 + n2); 
-};
-
-// 3D simplex noise 
-SimplexNoise.prototype.noise3d = function(xin, yin, zin) { 
-	var n0, n1, n2, n3; // Noise contributions from the four corners 
-  // Skew the input space to determine which simplex cell we're in 
-	var F3 = 1.0 / 3.0; 
-	var s = (xin + yin + zin) * F3; // Very nice and simple skew factor for 3D 
-	var i = Math.floor(xin + s); 
-	var j = Math.floor(yin + s); 
-	var k = Math.floor(zin + s); 
-	var G3 = 1.0 / 6.0; // Very nice and simple unskew factor, too 
-	var t = (i + j + k) * G3; 
-	var X0 = i - t; // Unskew the cell origin back to (x,y,z) space 
-	var Y0 = j - t; 
-	var Z0 = k - t; 
-	var x0 = xin - X0; // The x,y,z distances from the cell origin 
-	var y0 = yin - Y0; 
-	var z0 = zin - Z0; 
-  // For the 3D case, the simplex shape is a slightly irregular tetrahedron. 
-  // Determine which simplex we are in. 
-	var i1, j1, k1; // Offsets for second corner of simplex in (i,j,k) coords 
-	var i2, j2, k2; // Offsets for third corner of simplex in (i,j,k) coords 
-	if (x0 >= y0) { 
-		if (y0 >= z0) 
-      { i1 = 1; j1 = 0; k1 = 0; i2 = 1; j2 = 1; k2 = 0; } // X Y Z order 
-      else if (x0 >= z0) { i1 = 1; j1 = 0; k1 = 0; i2 = 1; j2 = 0; k2 = 1; } // X Z Y order 
-		else { i1 = 0; j1 = 0; k1 = 1; i2 = 1; j2 = 0; k2 = 1; } // Z X Y order 
-	} 
-	else { // x0<y0 
-		if (y0 < z0) { i1 = 0; j1 = 0; k1 = 1; i2 = 0; j2 = 1; k2 = 1; } // Z Y X order 
-    else if (x0 < z0) { i1 = 0; j1 = 1; k1 = 0; i2 = 0; j2 = 1; k2 = 1; } // Y Z X order 
-		else { i1 = 0; j1 = 1; k1 = 0; i2 = 1; j2 = 1; k2 = 0; } // Y X Z order 
-	} 
-  // A step of (1,0,0) in (i,j,k) means a step of (1-c,-c,-c) in (x,y,z), 
-  // a step of (0,1,0) in (i,j,k) means a step of (-c,1-c,-c) in (x,y,z), and 
-  // a step of (0,0,1) in (i,j,k) means a step of (-c,-c,1-c) in (x,y,z), where 
-  // c = 1/6.
-	var x1 = x0 - i1 + G3; // Offsets for second corner in (x,y,z) coords 
-	var y1 = y0 - j1 + G3; 
-	var z1 = z0 - k1 + G3; 
-	var x2 = x0 - i2 + 2.0 * G3; // Offsets for third corner in (x,y,z) coords 
-	var y2 = y0 - j2 + 2.0 * G3; 
-	var z2 = z0 - k2 + 2.0 * G3; 
-	var x3 = x0 - 1.0 + 3.0 * G3; // Offsets for last corner in (x,y,z) coords 
-	var y3 = y0 - 1.0 + 3.0 * G3; 
-	var z3 = z0 - 1.0 + 3.0 * G3; 
-  // Work out the hashed gradient indices of the four simplex corners 
-	var ii = i & 255; 
-	var jj = j & 255; 
-	var kk = k & 255; 
-	var gi0 = this.perm[ii + this.perm[jj + this.perm[kk]]] % 12; 
-	var gi1 = this.perm[ii + i1 + this.perm[jj + j1 + this.perm[kk + k1]]] % 12; 
-	var gi2 = this.perm[ii + i2 + this.perm[jj + j2 + this.perm[kk + k2]]] % 12; 
-	var gi3 = this.perm[ii + 1 + this.perm[jj + 1 + this.perm[kk + 1]]] % 12; 
-  // Calculate the contribution from the four corners 
-	var t0 = 0.6 - x0 * x0 - y0 * y0 - z0 * z0; 
-	if (t0 < 0) n0 = 0.0; 
-	else { 
-		t0 *= t0; 
-		n0 = t0 * t0 * this.dot3(this.grad3[gi0], x0, y0, z0); 
-	}
-	var t1 = 0.6 - x1 * x1 - y1 * y1 - z1 * z1; 
-	if (t1 < 0) n1 = 0.0; 
-	else { 
-		t1 *= t1; 
-		n1 = t1 * t1 * this.dot3(this.grad3[gi1], x1, y1, z1); 
-	} 
-	var t2 = 0.6 - x2 * x2 - y2 * y2 - z2 * z2; 
-	if (t2 < 0) n2 = 0.0; 
-	else { 
-		t2 *= t2; 
-		n2 = t2 * t2 * this.dot3(this.grad3[gi2], x2, y2, z2); 
-	} 
-	var t3 = 0.6 - x3 * x3 - y3 * y3 - z3 * z3; 
-	if (t3 < 0) n3 = 0.0; 
-	else { 
-		t3 *= t3; 
-		n3 = t3 * t3 * this.dot3(this.grad3[gi3], x3, y3, z3); 
-	} 
-  // Add contributions from each corner to get the final noise value. 
-  // The result is scaled to stay just inside [-1,1] 
-	return 32.0 * (n0 + n1 + n2 + n3); 
-};
-
-// 4D simplex noise
-SimplexNoise.prototype.noise4d = function( x, y, z, w ) {
-	// For faster and easier lookups
-	var grad4 = this.grad4;
-	var simplex = this.simplex;
-	var perm = this.perm;
-	
-   // The skewing and unskewing factors are hairy again for the 4D case
-	var F4 = (Math.sqrt(5.0) - 1.0) / 4.0;
-	var G4 = (5.0 - Math.sqrt(5.0)) / 20.0;
-	var n0, n1, n2, n3, n4; // Noise contributions from the five corners
-   // Skew the (x,y,z,w) space to determine which cell of 24 simplices we're in
-	var s = (x + y + z + w) * F4; // Factor for 4D skewing
-	var i = Math.floor(x + s);
-	var j = Math.floor(y + s);
-	var k = Math.floor(z + s);
-	var l = Math.floor(w + s);
-	var t = (i + j + k + l) * G4; // Factor for 4D unskewing
-	var X0 = i - t; // Unskew the cell origin back to (x,y,z,w) space
-	var Y0 = j - t;
-	var Z0 = k - t;
-	var W0 = l - t;
-	var x0 = x - X0;  // The x,y,z,w distances from the cell origin
-	var y0 = y - Y0;
-	var z0 = z - Z0;
-	var w0 = w - W0;
-
-   // For the 4D case, the simplex is a 4D shape I won't even try to describe.
-   // To find out which of the 24 possible simplices we're in, we need to
-   // determine the magnitude ordering of x0, y0, z0 and w0.
-   // The method below is a good way of finding the ordering of x,y,z,w and
-   // then find the correct traversal order for the simplex we’re in.
-   // First, six pair-wise comparisons are performed between each possible pair
-   // of the four coordinates, and the results are used to add up binary bits
-   // for an integer index.
-	var c1 = (x0 > y0) ? 32 : 0;
-	var c2 = (x0 > z0) ? 16 : 0;
-	var c3 = (y0 > z0) ? 8 : 0;
-	var c4 = (x0 > w0) ? 4 : 0;
-	var c5 = (y0 > w0) ? 2 : 0;
-	var c6 = (z0 > w0) ? 1 : 0;
-	var c = c1 + c2 + c3 + c4 + c5 + c6;
-	var i1, j1, k1, l1; // The integer offsets for the second simplex corner
-	var i2, j2, k2, l2; // The integer offsets for the third simplex corner
-	var i3, j3, k3, l3; // The integer offsets for the fourth simplex corner
-   // simplex[c] is a 4-vector with the numbers 0, 1, 2 and 3 in some order.
-   // Many values of c will never occur, since e.g. x>y>z>w makes x<z, y<w and x<w
-   // impossible. Only the 24 indices which have non-zero entries make any sense.
-   // We use a thresholding to set the coordinates in turn from the largest magnitude.
-   // The number 3 in the "simplex" array is at the position of the largest coordinate.
-	i1 = simplex[c][0] >= 3 ? 1 : 0;
-	j1 = simplex[c][1] >= 3 ? 1 : 0;
-	k1 = simplex[c][2] >= 3 ? 1 : 0;
-	l1 = simplex[c][3] >= 3 ? 1 : 0;
-   // The number 2 in the "simplex" array is at the second largest coordinate.
-	i2 = simplex[c][0] >= 2 ? 1 : 0;
-	j2 = simplex[c][1] >= 2 ? 1 : 0;    k2 = simplex[c][2] >= 2 ? 1 : 0;
-	l2 = simplex[c][3] >= 2 ? 1 : 0;
-   // The number 1 in the "simplex" array is at the second smallest coordinate.
-	i3 = simplex[c][0] >= 1 ? 1 : 0;
-	j3 = simplex[c][1] >= 1 ? 1 : 0;
-	k3 = simplex[c][2] >= 1 ? 1 : 0;
-	l3 = simplex[c][3] >= 1 ? 1 : 0;
-   // The fifth corner has all coordinate offsets = 1, so no need to look that up.
-	var x1 = x0 - i1 + G4; // Offsets for second corner in (x,y,z,w) coords
-	var y1 = y0 - j1 + G4;
-	var z1 = z0 - k1 + G4;
-	var w1 = w0 - l1 + G4;
-	var x2 = x0 - i2 + 2.0 * G4; // Offsets for third corner in (x,y,z,w) coords
-	var y2 = y0 - j2 + 2.0 * G4;
-	var z2 = z0 - k2 + 2.0 * G4;
-	var w2 = w0 - l2 + 2.0 * G4;
-	var x3 = x0 - i3 + 3.0 * G4; // Offsets for fourth corner in (x,y,z,w) coords
-	var y3 = y0 - j3 + 3.0 * G4;
-	var z3 = z0 - k3 + 3.0 * G4;
-	var w3 = w0 - l3 + 3.0 * G4;
-	var x4 = x0 - 1.0 + 4.0 * G4; // Offsets for last corner in (x,y,z,w) coords
-	var y4 = y0 - 1.0 + 4.0 * G4;
-	var z4 = z0 - 1.0 + 4.0 * G4;
-	var w4 = w0 - 1.0 + 4.0 * G4;
-   // Work out the hashed gradient indices of the five simplex corners
-	var ii = i & 255;
-	var jj = j & 255;
-	var kk = k & 255;
-	var ll = l & 255;
-	var gi0 = perm[ii + perm[jj + perm[kk + perm[ll]]]] % 32;
-	var gi1 = perm[ii + i1 + perm[jj + j1 + perm[kk + k1 + perm[ll + l1]]]] % 32;
-	var gi2 = perm[ii + i2 + perm[jj + j2 + perm[kk + k2 + perm[ll + l2]]]] % 32;
-	var gi3 = perm[ii + i3 + perm[jj + j3 + perm[kk + k3 + perm[ll + l3]]]] % 32;
-	var gi4 = perm[ii + 1 + perm[jj + 1 + perm[kk + 1 + perm[ll + 1]]]] % 32;
-   // Calculate the contribution from the five corners
-	var t0 = 0.6 - x0 * x0 - y0 * y0 - z0 * z0 - w0 * w0;
-	if (t0 < 0) n0 = 0.0;
-	else {
-		t0 *= t0;
-		n0 = t0 * t0 * this.dot4(grad4[gi0], x0, y0, z0, w0);
-	}
-	var t1 = 0.6 - x1 * x1 - y1 * y1 - z1 * z1 - w1 * w1;
-	if (t1 < 0) n1 = 0.0;
-	else {
-		t1 *= t1;
-		n1 = t1 * t1 * this.dot4(grad4[gi1], x1, y1, z1, w1);
-	}
-	var t2 = 0.6 - x2 * x2 - y2 * y2 - z2 * z2 - w2 * w2;
-	if (t2 < 0) n2 = 0.0;
-	else {
-		t2 *= t2;
-		n2 = t2 * t2 * this.dot4(grad4[gi2], x2, y2, z2, w2);
-	}   var t3 = 0.6 - x3 * x3 - y3 * y3 - z3 * z3 - w3 * w3;
-	if (t3 < 0) n3 = 0.0;
-	else {
-		t3 *= t3;
-		n3 = t3 * t3 * this.dot4(grad4[gi3], x3, y3, z3, w3);
-	}
-	var t4 = 0.6 - x4 * x4 - y4 * y4 - z4 * z4 - w4 * w4;
-	if (t4 < 0) n4 = 0.0;
-	else {
-		t4 *= t4;
-		n4 = t4 * t4 * this.dot4(grad4[gi4], x4, y4, z4, w4);
-	}
-   // Sum up and scale the result to cover the range [-1,1]
-	return 27.0 * (n0 + n1 + n2 + n3 + n4);
-};

+ 0 - 0
examples/js/AnimationClipCreator.js → examples/js/animation/AnimationClipCreator.js


+ 21 - 27
examples/js/TimelinerController.js → examples/js/animation/TimelinerController.js

@@ -3,7 +3,7 @@
  *
  * Timeliner GUI library (required to use this class):
  *
- * 		./libs/timeliner_gui.min.js
+ * 		../libs/timeliner_gui.min.js
  *
  * Source code:
  *
@@ -35,7 +35,7 @@ THREE.TimelinerController.prototype = {
 
 	constructor: THREE.TimelinerController,
 
-	init: function( timeliner ) {
+	init: function () {
 
 		var tracks = [],
 			trackInfo = this._trackInfo;
@@ -44,9 +44,8 @@ THREE.TimelinerController.prototype = {
 
 			var spec = trackInfo[ i ];
 
-			tracks.push( this._addTrack(
-					spec.type, spec.propertyPath,
-					spec.initialValue, spec.interpolation ) );
+			tracks.push( this._addTrack( spec.type, spec.propertyPath, spec.initialValue, spec.interpolation ) );
+
 		}
 
 		this._clip = new THREE.AnimationClip( 'editclip', 0, tracks );
@@ -54,7 +53,7 @@ THREE.TimelinerController.prototype = {
 
 	},
 
-	setDisplayTime: function( time ) {
+	setDisplayTime: function ( time ) {
 
 		this._action.time = time;
 		this._mixer.update( 0 );
@@ -63,25 +62,25 @@ THREE.TimelinerController.prototype = {
 
 	},
 
-	setDuration: function( duration ) {
+	setDuration: function ( duration ) {
 
 		this._clip.duration = duration;
 
 	},
 
-	getChannelNames: function() {
+	getChannelNames: function () {
 
 		return this._channelNames;
 
 	},
 
-	getChannelKeyTimes: function( channelName ) {
+	getChannelKeyTimes: function ( channelName ) {
 
 		return this._tracks[ channelName ].times;
 
 	},
 
-	setKeyframe: function( channelName, time ) {
+	setKeyframe: function ( channelName, time ) {
 
 		var track = this._tracks[ channelName ],
 			times = track.times,
@@ -106,8 +105,7 @@ THREE.TimelinerController.prototype = {
 
 			}
 
-			for ( var i = nValues - 1,
-					e = offset + stride - 1; i !== e; -- i ) {
+			for ( var i = nValues - 1, e = offset + stride - 1; i !== e; -- i ) {
 
 				values[ i ] = values[ i - stride ];
 
@@ -120,7 +118,7 @@ THREE.TimelinerController.prototype = {
 
 	},
 
-	delKeyframe: function( channelName, time ) {
+	delKeyframe: function ( channelName, time ) {
 
 		var track = this._tracks[ channelName ],
 			times = track.times,
@@ -159,7 +157,7 @@ THREE.TimelinerController.prototype = {
 
 	},
 
-	moveKeyframe: function( channelName, time, delta, moveRemaining ) {
+	moveKeyframe: function ( channelName, time, delta, moveRemaining ) {
 
 		var track = this._tracks[ channelName ],
 			times = track.times,
@@ -179,7 +177,7 @@ THREE.TimelinerController.prototype = {
 
 	},
 
-	serialize: function() {
+	serialize: function () {
 
 		var result = {
 				duration: this._clip.duration,
@@ -209,7 +207,7 @@ THREE.TimelinerController.prototype = {
 
 	},
 
-	deserialize: function( structs ) {
+	deserialize: function ( structs ) {
 
 		var names = this._channelNames,
 			tracks = this._tracks,
@@ -234,33 +232,29 @@ THREE.TimelinerController.prototype = {
 
 	},
 
-	_sort: function( track ) {
+	_sort: function ( track ) {
 
-		var times = track.times,
-			order = THREE.AnimationUtils.getKeyframeOrder( times );
+		var times = track.times, order = THREE.AnimationUtils.getKeyframeOrder( times );
 
-		this._setArray( times,
-				THREE.AnimationUtils.sortedArray( times, 1, order ) );
+		this._setArray( times, THREE.AnimationUtils.sortedArray( times, 1, order ) );
 
 		var values = track.values,
 			stride = track.getValueSize();
 
-		this._setArray( values,
-				THREE.AnimationUtils.sortedArray( values, stride, order ) );
+		this._setArray( values, THREE.AnimationUtils.sortedArray( values, stride, order ) );
 
 	},
 
-	_setArray: function( dst, src ) {
+	_setArray: function ( dst, src ) {
 
 		dst.length = 0;
 		dst.push.apply( dst, src );
 
 	},
 
-	_addTrack: function( type, prop, initialValue, interpolation ) {
+	_addTrack: function ( type, prop, initialValue, interpolation ) {
 
-		var track = new type(
-				prop, [ 0 ], initialValue, interpolation );
+		var track = new type( prop, [ 0 ], initialValue, interpolation );
 
 		// data must be in JS arrays so it can be resized
 		track.times = Array.prototype.slice.call( track.times );

+ 0 - 39
examples/js/crossfade/gui.js

@@ -1,39 +0,0 @@
-var transitionParams = {
-	"useTexture": true,
-	"transition": 0.5,
-	"transitionSpeed": 2.0,
-	"texture": 5,
-	"loopTexture": true,
-	"animateTransition": true,
-	"textureThreshold": 0.3
-};
-
-function initGUI() {
-	
-	var gui = new dat.GUI();
-
-	gui.add( transitionParams, "useTexture" ).onChange( function( value ) {
-		
-		transition.useTexture( value );
-		
-	} );
-	
-	gui.add( transitionParams, 'loopTexture' );
-	
-	gui.add( transitionParams, 'texture', { Perlin: 0, Squares: 1, Cells: 2, Distort: 3, Gradient: 4, Radial: 5 } ).onChange( function( value ) {
-		
-		transition.setTexture( value );
-		
-	} ).listen();
-		
-	gui.add( transitionParams, "textureThreshold", 0, 1, 0.01 ).onChange( function( value ) {
-		
-		transition.setTextureThreshold( value );
-		
-	} );
-
-	gui.add( transitionParams, "animateTransition" );
-	gui.add( transitionParams, "transition", 0, 1, 0.01 ).listen();
-	gui.add( transitionParams, "transitionSpeed", 0.5, 5, 0.01 );
-	
-}

+ 0 - 119
examples/js/crossfade/scenes.js

@@ -1,119 +0,0 @@
-function generateGeometry( objectType, numObjects ) {
-
-	function applyVertexColors( geometry, color ) {
-
-		var position = geometry.attributes.position;
-		var colors = [];
-
-		for ( var i = 0; i < position.count; i ++ ) {
-
-			colors.push( color.r, color.g, color.b );
-
-		}
-
-		geometry.addAttribute( 'color', new THREE.Float32BufferAttribute( colors, 3 ) );
-
-	}
-
-	var geometries = [];
-
-	var matrix = new THREE.Matrix4();
-	var position = new THREE.Vector3();
-	var rotation = new THREE.Euler();
-	var quaternion = new THREE.Quaternion();
-	var scale = new THREE.Vector3();
-	var color = new THREE.Color();
-
-	for ( var i = 0; i < numObjects; i ++ ) {
-
-		position.x = Math.random() * 10000 - 5000;
-		position.y = Math.random() * 6000 - 3000;
-		position.z = Math.random() * 8000 - 4000;
-
-		rotation.x = Math.random() * 2 * Math.PI;
-		rotation.y = Math.random() * 2 * Math.PI;
-		rotation.z = Math.random() * 2 * Math.PI;
-		quaternion.setFromEuler( rotation, false );
-
-		scale.x = Math.random() * 200 + 100;
-
-		var geometry;
-
-		if ( objectType === 'cube' ) {
-
-			geometry = new THREE.BoxBufferGeometry( 1, 1, 1 );
-			geometry = geometry.toNonIndexed(); // merging needs consistent buffer geometries
-			scale.y = Math.random() * 200 + 100;
-			scale.z = Math.random() * 200 + 100;
-			color.setRGB( 0, 0, 0.1 + 0.9 * Math.random() );
-
-		} else if ( objectType === 'sphere' ) {
-
-			geometry = new THREE.IcosahedronBufferGeometry( 1, 1 );
-			scale.y = scale.z = scale.x;
-			color.setRGB( 0.1 + 0.9 * Math.random(), 0, 0 );
-
-		}
-
-		// give the geom's vertices a random color, to be displayed
-		applyVertexColors( geometry, color );
-
-		matrix.compose( position, quaternion, scale );
-		geometry.applyMatrix( matrix );
-
-		geometries.push( geometry );
-
-	}
-
-	return THREE.BufferGeometryUtils.mergeBufferGeometries( geometries );
-
-}
-
-function Scene( type, numObjects, cameraZ, fov, rotationSpeed, clearColor ) {
-
-	this.clearColor = clearColor;
-
-	this.camera = new THREE.PerspectiveCamera( fov, window.innerWidth / window.innerHeight, 1, 10000 );
-	this.camera.position.z = cameraZ;
-
-	// Setup scene
-	this.scene = new THREE.Scene();
-	this.scene.add( new THREE.AmbientLight( 0x555555 ) );
-
-	var light = new THREE.SpotLight( 0xffffff, 1.5 );
-	light.position.set( 0, 500, 2000 );
-	this.scene.add( light );
-
-	this.rotationSpeed = rotationSpeed;
-
-	var defaultMaterial = new THREE.MeshPhongMaterial( { color: 0xffffff, flatShading: true, vertexColors: THREE.VertexColors } );
-	this.mesh = new THREE.Mesh( generateGeometry( type, numObjects ), defaultMaterial );
-	this.scene.add( this.mesh );
-
-	var renderTargetParameters = { minFilter: THREE.LinearFilter, magFilter: THREE.LinearFilter, format: THREE.RGBFormat, stencilBuffer: false };
-	this.fbo = new THREE.WebGLRenderTarget( window.innerWidth, window.innerHeight, renderTargetParameters );
-
-	this.render = function ( delta, rtt ) {
-
-		this.mesh.rotation.x += delta * this.rotationSpeed.x;
-		this.mesh.rotation.y += delta * this.rotationSpeed.y;
-		this.mesh.rotation.z += delta * this.rotationSpeed.z;
-
-		renderer.setClearColor( this.clearColor );
-
-		if ( rtt ) {
-
-			renderer.setRenderTarget( this.fbo );
-			renderer.clear();
-			renderer.render( this.scene, this.camera );
-
-		} else {
-
-			renderer.setRenderTarget( null );
-			renderer.render( this.scene, this.camera );
-
-		}
-
-	};
-
-}

+ 0 - 168
examples/js/crossfade/transition.js

@@ -1,168 +0,0 @@
-function Transition( sceneA, sceneB ) {
-
-	this.scene = new THREE.Scene();
-
-	this.cameraOrtho = new THREE.OrthographicCamera( window.innerWidth / - 2, window.innerWidth / 2, window.innerHeight / 2, window.innerHeight / - 2, - 10, 10 );
-
-	this.textures = [];
-
-	var loader = new THREE.TextureLoader();
-
-	for ( var i = 0; i < 6; i ++ )
-		this.textures[ i ] = loader.load( 'textures/transition/transition' + ( i + 1 ) + '.png' );
-
-	this.quadmaterial = new THREE.ShaderMaterial( {
-
-		uniforms: {
-
-			tDiffuse1: {
-				value: null
-			},
-			tDiffuse2: {
-				value: null
-			},
-			mixRatio: {
-				value: 0.0
-			},
-			threshold: {
-				value: 0.1
-			},
-			useTexture: {
-				value: 1
-			},
-			tMixTexture: {
-				value: this.textures[ 0 ]
-			}
-		},
-		vertexShader: [
-
-			"varying vec2 vUv;",
-
-			"void main() {",
-
-			"vUv = vec2( uv.x, uv.y );",
-			"gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
-
-			"}"
-
-		].join( "\n" ),
-		fragmentShader: [
-
-			"uniform float mixRatio;",
-
-			"uniform sampler2D tDiffuse1;",
-			"uniform sampler2D tDiffuse2;",
-			"uniform sampler2D tMixTexture;",
-
-			"uniform int useTexture;",
-			"uniform float threshold;",
-
-			"varying vec2 vUv;",
-
-			"void main() {",
-
-			"	vec4 texel1 = texture2D( tDiffuse1, vUv );",
-			"	vec4 texel2 = texture2D( tDiffuse2, vUv );",
-
-			"	if (useTexture==1) {",
-
-			"		vec4 transitionTexel = texture2D( tMixTexture, vUv );",
-			"		float r = mixRatio * (1.0 + threshold * 2.0) - threshold;",
-			"		float mixf=clamp((transitionTexel.r - r)*(1.0/threshold), 0.0, 1.0);",
-
-			"		gl_FragColor = mix( texel1, texel2, mixf );",
-
-			"	} else {",
-
-			"		gl_FragColor = mix( texel2, texel1, mixRatio );",
-
-			"	}",
-
-			"}"
-
-		].join( "\n" )
-
-	} );
-
-	var quadgeometry = new THREE.PlaneBufferGeometry( window.innerWidth, window.innerHeight );
-
-	this.quad = new THREE.Mesh( quadgeometry, this.quadmaterial );
-	this.scene.add( this.quad );
-
-	// Link both scenes and their FBOs
-	this.sceneA = sceneA;
-	this.sceneB = sceneB;
-
-	this.quadmaterial.uniforms.tDiffuse1.value = sceneA.fbo.texture;
-	this.quadmaterial.uniforms.tDiffuse2.value = sceneB.fbo.texture;
-
-	this.needChange = false;
-
-	this.setTextureThreshold = function ( value ) {
-
-		this.quadmaterial.uniforms.threshold.value = value;
-
-	};
-
-	this.useTexture = function ( value ) {
-
-		this.quadmaterial.uniforms.useTexture.value = value ? 1 : 0;
-
-	};
-
-	this.setTexture = function ( i ) {
-
-		this.quadmaterial.uniforms.tMixTexture.value = this.textures[ i ];
-
-	};
-
-	this.render = function ( delta ) {
-
-		// Transition animation
-		if ( transitionParams.animateTransition ) {
-
-			var t = ( 1 + Math.sin( transitionParams.transitionSpeed * clock.getElapsedTime() / Math.PI ) ) / 2;
-			transitionParams.transition = THREE.Math.smoothstep( t, 0.3, 0.7 );
-
-			// Change the current alpha texture after each transition
-			if ( transitionParams.loopTexture && ( transitionParams.transition == 0 || transitionParams.transition == 1 ) ) {
-
-				if ( this.needChange ) {
-
-					transitionParams.texture = ( transitionParams.texture + 1 ) % this.textures.length;
-					this.quadmaterial.uniforms.tMixTexture.value = this.textures[ transitionParams.texture ];
-					this.needChange = false;
-
-				}
-
-			} else
-				this.needChange = true;
-
-		}
-
-		this.quadmaterial.uniforms.mixRatio.value = transitionParams.transition;
-
-		// Prevent render both scenes when it's not necessary
-		if ( transitionParams.transition == 0 ) {
-
-			this.sceneB.render( delta, false );
-
-		} else if ( transitionParams.transition == 1 ) {
-
-			this.sceneA.render( delta, false );
-
-		} else {
-
-			// When 0<transition<1 render transition between two scenes
-
-			this.sceneA.render( delta, true );
-			this.sceneB.render( delta, true );
-			renderer.setRenderTarget( null );
-			renderer.clear();
-			renderer.render( this.scene, this.cameraOrtho );
-
-		}
-
-	};
-
-}

+ 19 - 19
examples/js/CurveExtras.js → examples/js/curves/CurveExtras.js

@@ -11,7 +11,7 @@
  * http://prideout.net/blog/?p=44
  */
 
-( function ( Curves ) {
+THREE.Curves = ( function () {
 
 	// GrannyKnot
 
@@ -405,21 +405,21 @@
 
 	};
 
-	// export
-
-	Curves.GrannyKnot = GrannyKnot;
-	Curves.HeartCurve = HeartCurve;
-	Curves.VivianiCurve = VivianiCurve;
-	Curves.KnotCurve = KnotCurve;
-	Curves.HelixCurve = HelixCurve;
-	Curves.TrefoilKnot = TrefoilKnot;
-	Curves.TorusKnot = TorusKnot;
-	Curves.CinquefoilKnot = CinquefoilKnot;
-	Curves.TrefoilPolynomialKnot = TrefoilPolynomialKnot;
-	Curves.FigureEightPolynomialKnot = FigureEightPolynomialKnot;
-	Curves.DecoratedTorusKnot4a = DecoratedTorusKnot4a;
-	Curves.DecoratedTorusKnot4b = DecoratedTorusKnot4b;
-	Curves.DecoratedTorusKnot5a = DecoratedTorusKnot5a;
-	Curves.DecoratedTorusKnot5c = DecoratedTorusKnot5c;
-
-} )( THREE.Curves = THREE.Curves || {} );
+	return {
+		GrannyKnot: GrannyKnot,
+		HeartCurve: HeartCurve,
+		VivianiCurve: VivianiCurve,
+		KnotCurve: KnotCurve,
+		HelixCurve: HelixCurve,
+		TrefoilKnot: TrefoilKnot,
+		TorusKnot: TorusKnot,
+		CinquefoilKnot: CinquefoilKnot,
+		TrefoilPolynomialKnot: TrefoilPolynomialKnot,
+		FigureEightPolynomialKnot: FigureEightPolynomialKnot,
+		DecoratedTorusKnot4a: DecoratedTorusKnot4a,
+		DecoratedTorusKnot4b: DecoratedTorusKnot4b,
+		DecoratedTorusKnot5a: DecoratedTorusKnot5a,
+		DecoratedTorusKnot5c: DecoratedTorusKnot5c
+	};
+
+} )();

+ 6 - 0
examples/js/effects/AsciiEffect.js

@@ -159,11 +159,13 @@ THREE.AsciiEffect = function ( renderer, charSet, options ) {
 	if ( strResolution == "low" ) {
 
 		switch ( iScale ) {
+
 			case 1 : fLetterSpacing = - 1; break;
 			case 2 :
 			case 3 : fLetterSpacing = - 2.1; break;
 			case 4 : fLetterSpacing = - 3.1; break;
 			case 5 : fLetterSpacing = - 4.15; break;
+
 		}
 
 	}
@@ -171,11 +173,13 @@ THREE.AsciiEffect = function ( renderer, charSet, options ) {
 	if ( strResolution == "medium" ) {
 
 		switch ( iScale ) {
+
 			case 1 : fLetterSpacing = 0; break;
 			case 2 : fLetterSpacing = - 1; break;
 			case 3 : fLetterSpacing = - 1.04; break;
 			case 4 :
 			case 5 : fLetterSpacing = - 2.1; break;
+
 		}
 
 	}
@@ -183,11 +187,13 @@ THREE.AsciiEffect = function ( renderer, charSet, options ) {
 	if ( strResolution == "high" ) {
 
 		switch ( iScale ) {
+
 			case 1 :
 			case 2 : fLetterSpacing = 0; break;
 			case 3 :
 			case 4 :
 			case 5 : fLetterSpacing = - 1; break;
+
 		}
 
 	}

+ 11 - 14
examples/js/effects/OutlineEffect.js

@@ -108,8 +108,6 @@ THREE.OutlineEffect = function ( renderer, parameters ) {
 
 	var vertexShaderChunk = [
 
-		"#include <fog_pars_vertex>",
-
 		"uniform float outlineThickness;",
 
 		"vec4 calculateOutline( vec4 pos, vec3 objectNormal, vec4 skinned ) {",
@@ -175,7 +173,6 @@ THREE.OutlineEffect = function ( renderer, parameters ) {
 
 		var shaderID = shaderIDs[ originalMaterial.type ];
 		var originalUniforms, originalVertexShader;
-		var outlineParameters = originalMaterial.userData.outlineParameters;
 
 		if ( shaderID !== undefined ) {
 
@@ -212,15 +209,15 @@ THREE.OutlineEffect = function ( renderer, parameters ) {
 		var uniforms = Object.assign( {}, originalUniforms, uniformsChunk );
 
 		var vertexShader = originalVertexShader
-					// put vertexShaderChunk right before "void main() {...}"
-					.replace( /void\s+main\s*\(\s*\)/, vertexShaderChunk + '\nvoid main()' )
-					// put vertexShaderChunk2 the end of "void main() {...}"
-					// Note: here assums originalVertexShader ends with "}" of "void main() {...}"
-					.replace( /\}\s*$/, vertexShaderChunk2 + '\n}' )
-					// remove any light related lines
-					// Note: here is very sensitive to originalVertexShader
-					// TODO: consider safer way
-					.replace( /#include\s+<[\w_]*light[\w_]*>/g, '' );
+			// put vertexShaderChunk right before "void main() {...}"
+			.replace( /void\s+main\s*\(\s*\)/, vertexShaderChunk + '\nvoid main()' )
+			// put vertexShaderChunk2 the end of "void main() {...}"
+			// Note: here assums originalVertexShader ends with "}" of "void main() {...}"
+			.replace( /\}\s*$/, vertexShaderChunk2 + '\n}' )
+			// remove any light related lines
+			// Note: here is very sensitive to originalVertexShader
+			// TODO: consider safer way
+			.replace( /#include\s+<[\w_]*light[\w_]*>/g, '' );
 
 		var defines = {};
 
@@ -322,7 +319,7 @@ THREE.OutlineEffect = function ( renderer, parameters ) {
 
 	}
 
-	function onBeforeRender( renderer, scene, camera, geometry, material, group ) {
+	function onBeforeRender( renderer, scene, camera, geometry, material ) {
 
 		var originalMaterial = originalMaterials[ material.uuid ];
 
@@ -418,7 +415,7 @@ THREE.OutlineEffect = function ( renderer, parameters ) {
 
 			if ( cache[ key ].used === false ) {
 
-				cache[ key ].count++;
+				cache[ key ].count ++;
 
 				if ( cache[ key ].keepAlive === false && cache[ key ].count > removeThresholdCount ) {
 

+ 13 - 13
examples/js/geometries/BoxLineGeometry.js

@@ -28,10 +28,10 @@ THREE.BoxLineGeometry = function ( width, height, depth, widthSegments, heightSe
 
 	for ( var i = 0; i <= widthSegments; i ++ ) {
 
-		vertices.push( x, - heightHalf, - depthHalf, x,   heightHalf, - depthHalf );
-		vertices.push( x,   heightHalf, - depthHalf, x,   heightHalf,   depthHalf );
-		vertices.push( x,   heightHalf,   depthHalf, x, - heightHalf,   depthHalf );
-		vertices.push( x, - heightHalf,   depthHalf, x, - heightHalf, - depthHalf );
+		vertices.push( x, - heightHalf, - depthHalf, x, heightHalf, - depthHalf );
+		vertices.push( x, heightHalf, - depthHalf, x, heightHalf, depthHalf );
+		vertices.push( x, heightHalf, depthHalf, x, - heightHalf, depthHalf );
+		vertices.push( x, - heightHalf, depthHalf, x, - heightHalf, - depthHalf );
 
 		x += segmentWidth;
 
@@ -39,10 +39,10 @@ THREE.BoxLineGeometry = function ( width, height, depth, widthSegments, heightSe
 
 	for ( var i = 0; i <= heightSegments; i ++ ) {
 
-		vertices.push( - widthHalf, y, - depthHalf,   widthHalf, y, - depthHalf );
-		vertices.push(   widthHalf, y, - depthHalf,   widthHalf, y,   depthHalf );
-		vertices.push(   widthHalf, y,   depthHalf, - widthHalf, y,   depthHalf );
-		vertices.push( - widthHalf, y,   depthHalf, - widthHalf, y, - depthHalf );
+		vertices.push( - widthHalf, y, - depthHalf, widthHalf, y, - depthHalf );
+		vertices.push( widthHalf, y, - depthHalf, widthHalf, y, depthHalf );
+		vertices.push( widthHalf, y, depthHalf, - widthHalf, y, depthHalf );
+		vertices.push( - widthHalf, y, depthHalf, - widthHalf, y, - depthHalf );
 
 		y += segmentHeight;
 
@@ -50,10 +50,10 @@ THREE.BoxLineGeometry = function ( width, height, depth, widthSegments, heightSe
 
 	for ( var i = 0; i <= depthSegments; i ++ ) {
 
-		vertices.push( - widthHalf, - heightHalf, z, - widthHalf,   heightHalf, z );
-		vertices.push( - widthHalf,   heightHalf, z,   widthHalf,   heightHalf, z );
-		vertices.push(   widthHalf,   heightHalf, z,   widthHalf, - heightHalf, z );
-		vertices.push(   widthHalf, - heightHalf, z, - widthHalf, - heightHalf, z );
+		vertices.push( - widthHalf, - heightHalf, z, - widthHalf, heightHalf, z );
+		vertices.push( - widthHalf, heightHalf, z, widthHalf, heightHalf, z );
+		vertices.push( widthHalf, heightHalf, z, widthHalf, - heightHalf, z );
+		vertices.push( widthHalf, - heightHalf, z, - widthHalf, - heightHalf, z );
 
 		z += segmentDepth;
 
@@ -61,7 +61,7 @@ THREE.BoxLineGeometry = function ( width, height, depth, widthSegments, heightSe
 
 	this.addAttribute( 'position', new THREE.Float32BufferAttribute( vertices, 3 ) );
 
-}
+};
 
 THREE.BoxLineGeometry.prototype = Object.create( THREE.BufferGeometry.prototype );
 THREE.BoxLineGeometry.prototype.constructor = THREE.BoxLineGeometry;

+ 36 - 47
examples/js/geometries/ConvexGeometry.js

@@ -2,80 +2,69 @@
  * @author Mugen87 / https://github.com/Mugen87
  */
 
-( function () {
+// ConvexGeometry
 
-	// ConvexGeometry
+THREE.ConvexGeometry = function ( points ) {
 
-	function ConvexGeometry( points ) {
+	THREE.Geometry.call( this );
 
-		THREE.Geometry.call( this );
+	this.fromBufferGeometry( new THREE.ConvexBufferGeometry( points ) );
+	this.mergeVertices();
 
-		this.fromBufferGeometry( new ConvexBufferGeometry( points ) );
-		this.mergeVertices();
+};
 
-	}
-
-	ConvexGeometry.prototype = Object.create( THREE.Geometry.prototype );
-	ConvexGeometry.prototype.constructor = ConvexGeometry;
-
-	// ConvexBufferGeometry
-
-	function ConvexBufferGeometry( points ) {
+THREE.ConvexGeometry.prototype = Object.create( THREE.Geometry.prototype );
+THREE.ConvexGeometry.prototype.constructor = THREE.ConvexGeometry;
 
-		THREE.BufferGeometry.call( this );
+// ConvexBufferGeometry
 
-		// buffers
+THREE.ConvexBufferGeometry = function ( points ) {
 
-		var vertices = [];
-		var normals = [];
+	THREE.BufferGeometry.call( this );
 
-		// execute QuickHull
+	// buffers
 
-		if ( THREE.QuickHull === undefined ) {
+	var vertices = [];
+	var normals = [];
 
-			console.error( 'THREE.ConvexBufferGeometry: ConvexBufferGeometry relies on THREE.QuickHull' );
+	if ( THREE.ConvexHull === undefined ) {
 
-		}
+		console.error( 'THREE.ConvexBufferGeometry: ConvexBufferGeometry relies on THREE.ConvexHull' );
 
-		var quickHull = new THREE.QuickHull().setFromPoints( points );
-
-		// generate vertices and normals
-
-		var faces = quickHull.faces;
+	}
 
-		for ( var i = 0; i < faces.length; i ++ ) {
+	var convexHull = new THREE.ConvexHull().setFromPoints( points );
 
-			var face = faces[ i ];
-			var edge = face.edge;
+	// generate vertices and normals
 
-			// we move along a doubly-connected edge list to access all face points (see HalfEdge docs)
+	var faces = convexHull.faces;
 
-			do {
+	for ( var i = 0; i < faces.length; i ++ ) {
 
-				var point = edge.head().point;
+		var face = faces[ i ];
+		var edge = face.edge;
 
-				vertices.push( point.x, point.y, point.z );
-				normals.push( face.normal.x, face.normal.y, face.normal.z );
+		// we move along a doubly-connected edge list to access all face points (see HalfEdge docs)
 
-				edge = edge.next;
+		do {
 
-			} while ( edge !== face.edge );
+			var point = edge.head().point;
 
-		}
+			vertices.push( point.x, point.y, point.z );
+			normals.push( face.normal.x, face.normal.y, face.normal.z );
 
-		// build geometry
+			edge = edge.next;
 
-		this.addAttribute( 'position', new THREE.Float32BufferAttribute( vertices, 3 ) );
-		this.addAttribute( 'normal', new THREE.Float32BufferAttribute( normals, 3 ) );
+		} while ( edge !== face.edge );
 
 	}
 
-	ConvexBufferGeometry.prototype = Object.create( THREE.BufferGeometry.prototype );
-	ConvexBufferGeometry.prototype.constructor = ConvexBufferGeometry;
+	// build geometry
 
-	// export
+	this.addAttribute( 'position', new THREE.Float32BufferAttribute( vertices, 3 ) );
+	this.addAttribute( 'normal', new THREE.Float32BufferAttribute( normals, 3 ) );
 
-	THREE.ConvexGeometry = ConvexGeometry;
-	THREE.ConvexBufferGeometry = ConvexBufferGeometry;
+};
 
-} )();
+THREE.ConvexBufferGeometry.prototype = Object.create( THREE.BufferGeometry.prototype );
+THREE.ConvexBufferGeometry.prototype.constructor = THREE.ConvexBufferGeometry;

+ 202 - 210
examples/js/geometries/DecalGeometry.js

@@ -16,342 +16,334 @@
  *
  */
 
-( function () {
+THREE.DecalGeometry = function ( mesh, position, orientation, size ) {
 
-	function DecalGeometry( mesh, position, orientation, size ) {
+	THREE.BufferGeometry.call( this );
 
-		THREE.BufferGeometry.call( this );
+	// buffers
 
-		// buffers
+	var vertices = [];
+	var normals = [];
+	var uvs = [];
 
-		var vertices = [];
-		var normals = [];
-		var uvs = [];
+	// helpers
 
-		// helpers
+	var plane = new THREE.Vector3();
 
-		var plane = new THREE.Vector3();
+	// this matrix represents the transformation of the decal projector
 
-		// this matrix represents the transformation of the decal projector
+	var projectorMatrix = new THREE.Matrix4();
+	projectorMatrix.makeRotationFromEuler( orientation );
+	projectorMatrix.setPosition( position );
 
-		var projectorMatrix = new THREE.Matrix4();
-		projectorMatrix.makeRotationFromEuler( orientation );
-		projectorMatrix.setPosition( position );
+	var projectorMatrixInverse = new THREE.Matrix4().getInverse( projectorMatrix );
 
-		var projectorMatrixInverse = new THREE.Matrix4().getInverse( projectorMatrix );
+	// generate buffers
 
-		// generate buffers
+	generate();
 
-		generate();
+	// build geometry
 
-		// build geometry
+	this.addAttribute( 'position', new THREE.Float32BufferAttribute( vertices, 3 ) );
+	this.addAttribute( 'normal', new THREE.Float32BufferAttribute( normals, 3 ) );
+	this.addAttribute( 'uv', new THREE.Float32BufferAttribute( uvs, 2 ) );
 
-		this.addAttribute( 'position', new THREE.Float32BufferAttribute( vertices, 3 ) );
-		this.addAttribute( 'normal', new THREE.Float32BufferAttribute( normals, 3 ) );
-		this.addAttribute( 'uv', new THREE.Float32BufferAttribute( uvs, 2 ) );
+	function generate() {
 
-		function generate() {
+		var i;
+		var geometry = new THREE.BufferGeometry();
+		var decalVertices = [];
 
-			var i;
-			var geometry = new THREE.BufferGeometry();
-			var decalVertices = [];
+		var vertex = new THREE.Vector3();
+		var normal = new THREE.Vector3();
 
-			var vertex = new THREE.Vector3();
-			var normal = new THREE.Vector3();
+		// handle different geometry types
 
-			// handle different geometry types
+		if ( mesh.geometry.isGeometry ) {
 
-			if ( mesh.geometry.isGeometry ) {
+			geometry.fromGeometry( mesh.geometry );
 
-				geometry.fromGeometry( mesh.geometry );
+		} else {
 
-			} else {
+			geometry.copy( mesh.geometry );
 
-				geometry.copy( mesh.geometry );
-
-			}
+		}
 
-			var positionAttribute = geometry.attributes.position;
-			var normalAttribute = geometry.attributes.normal;
+		var positionAttribute = geometry.attributes.position;
+		var normalAttribute = geometry.attributes.normal;
 
-			// first, create an array of 'DecalVertex' objects
-			// three consecutive 'DecalVertex' objects represent a single face
-			//
-			// this data structure will be later used to perform the clipping
+		// first, create an array of 'DecalVertex' objects
+		// three consecutive 'DecalVertex' objects represent a single face
+		//
+		// this data structure will be later used to perform the clipping
 
-			if ( geometry.index !== null ) {
+		if ( geometry.index !== null ) {
 
-				// indexed BufferGeometry
+			// indexed BufferGeometry
 
-				var index = geometry.index;
+			var index = geometry.index;
 
-				for ( i = 0; i < index.count; i ++ ) {
+			for ( i = 0; i < index.count; i ++ ) {
 
-					vertex.fromBufferAttribute( positionAttribute, index.getX( i ) );
-					normal.fromBufferAttribute( normalAttribute, index.getX( i ) );
+				vertex.fromBufferAttribute( positionAttribute, index.getX( i ) );
+				normal.fromBufferAttribute( normalAttribute, index.getX( i ) );
 
-					pushDecalVertex( decalVertices, vertex, normal );
+				pushDecalVertex( decalVertices, vertex, normal );
 
-				}
-
-			} else {
+			}
 
-				// non-indexed BufferGeometry
+		} else {
 
-				for ( i = 0; i < positionAttribute.count; i ++ ) {
+			// non-indexed BufferGeometry
 
-					vertex.fromBufferAttribute( positionAttribute, i );
-					normal.fromBufferAttribute( normalAttribute, i );
+			for ( i = 0; i < positionAttribute.count; i ++ ) {
 
-					pushDecalVertex( decalVertices, vertex, normal );
+				vertex.fromBufferAttribute( positionAttribute, i );
+				normal.fromBufferAttribute( normalAttribute, i );
 
-				}
+				pushDecalVertex( decalVertices, vertex, normal );
 
 			}
 
-			// second, clip the geometry so that it doesn't extend out from the projector
-
-			decalVertices = clipGeometry( decalVertices, plane.set( 1, 0, 0 ) );
-			decalVertices = clipGeometry( decalVertices, plane.set( - 1, 0, 0 ) );
-			decalVertices = clipGeometry( decalVertices, plane.set( 0, 1, 0 ) );
-			decalVertices = clipGeometry( decalVertices, plane.set( 0, - 1, 0 ) );
-			decalVertices = clipGeometry( decalVertices, plane.set( 0, 0, 1 ) );
-			decalVertices = clipGeometry( decalVertices, plane.set( 0, 0, - 1 ) );
-
-			// third, generate final vertices, normals and uvs
-
-			for ( i = 0; i < decalVertices.length; i ++ ) {
-
-				var decalVertex = decalVertices[ i ];
-
-				// create texture coordinates (we are still in projector space)
+		}
 
-				uvs.push(
-					0.5 + ( decalVertex.position.x / size.x ),
-					0.5 + ( decalVertex.position.y / size.y )
-				);
+		// second, clip the geometry so that it doesn't extend out from the projector
 
-				// transform the vertex back to world space
+		decalVertices = clipGeometry( decalVertices, plane.set( 1, 0, 0 ) );
+		decalVertices = clipGeometry( decalVertices, plane.set( - 1, 0, 0 ) );
+		decalVertices = clipGeometry( decalVertices, plane.set( 0, 1, 0 ) );
+		decalVertices = clipGeometry( decalVertices, plane.set( 0, - 1, 0 ) );
+		decalVertices = clipGeometry( decalVertices, plane.set( 0, 0, 1 ) );
+		decalVertices = clipGeometry( decalVertices, plane.set( 0, 0, - 1 ) );
 
-				decalVertex.position.applyMatrix4( projectorMatrix );
+		// third, generate final vertices, normals and uvs
 
-				// now create vertex and normal buffer data
+		for ( i = 0; i < decalVertices.length; i ++ ) {
 
-				vertices.push( decalVertex.position.x, decalVertex.position.y, decalVertex.position.z );
-				normals.push( decalVertex.normal.x, decalVertex.normal.y, decalVertex.normal.z );
+			var decalVertex = decalVertices[ i ];
 
-			}
+			// create texture coordinates (we are still in projector space)
 
-		}
+			uvs.push(
+				0.5 + ( decalVertex.position.x / size.x ),
+				0.5 + ( decalVertex.position.y / size.y )
+			);
 
-		function pushDecalVertex( decalVertices, vertex, normal ) {
+			// transform the vertex back to world space
 
-			// transform the vertex to world space, then to projector space
+			decalVertex.position.applyMatrix4( projectorMatrix );
 
-			vertex.applyMatrix4( mesh.matrixWorld );
-			vertex.applyMatrix4( projectorMatrixInverse );
+			// now create vertex and normal buffer data
 
-			decalVertices.push( new DecalVertex( vertex.clone(), normal.clone() ) );
+			vertices.push( decalVertex.position.x, decalVertex.position.y, decalVertex.position.z );
+			normals.push( decalVertex.normal.x, decalVertex.normal.y, decalVertex.normal.z );
 
 		}
 
-		function clipGeometry( inVertices, plane ) {
+	}
 
-			var outVertices = [];
+	function pushDecalVertex( decalVertices, vertex, normal ) {
 
-			var s = 0.5 * Math.abs( size.dot( plane ) );
+		// transform the vertex to world space, then to projector space
 
-			// a single iteration clips one face,
-			// which consists of three consecutive 'DecalVertex' objects
+		vertex.applyMatrix4( mesh.matrixWorld );
+		vertex.applyMatrix4( projectorMatrixInverse );
 
-			for ( var i = 0; i < inVertices.length; i += 3 ) {
+		decalVertices.push( new THREE.DecalVertex( vertex.clone(), normal.clone() ) );
 
-				var v1Out, v2Out, v3Out, total = 0;
-				var nV1, nV2, nV3, nV4;
+	}
 
-				var d1 = inVertices[ i + 0 ].position.dot( plane ) - s;
-				var d2 = inVertices[ i + 1 ].position.dot( plane ) - s;
-				var d3 = inVertices[ i + 2 ].position.dot( plane ) - s;
+	function clipGeometry( inVertices, plane ) {
 
-				v1Out = d1 > 0;
-				v2Out = d2 > 0;
-				v3Out = d3 > 0;
+		var outVertices = [];
 
-				// calculate, how many vertices of the face lie outside of the clipping plane
+		var s = 0.5 * Math.abs( size.dot( plane ) );
 
-				total = ( v1Out ? 1 : 0 ) + ( v2Out ? 1 : 0 ) + ( v3Out ? 1 : 0 );
+		// a single iteration clips one face,
+		// which consists of three consecutive 'DecalVertex' objects
 
-				switch ( total ) {
+		for ( var i = 0; i < inVertices.length; i += 3 ) {
 
-					case 0: {
+			var v1Out, v2Out, v3Out, total = 0;
+			var nV1, nV2, nV3, nV4;
 
-						// the entire face lies inside of the plane, no clipping needed
+			var d1 = inVertices[ i + 0 ].position.dot( plane ) - s;
+			var d2 = inVertices[ i + 1 ].position.dot( plane ) - s;
+			var d3 = inVertices[ i + 2 ].position.dot( plane ) - s;
 
-						outVertices.push( inVertices[ i ] );
-						outVertices.push( inVertices[ i + 1 ] );
-						outVertices.push( inVertices[ i + 2 ] );
-						break;
+			v1Out = d1 > 0;
+			v2Out = d2 > 0;
+			v3Out = d3 > 0;
 
-					}
+			// calculate, how many vertices of the face lie outside of the clipping plane
 
-					case 1: {
+			total = ( v1Out ? 1 : 0 ) + ( v2Out ? 1 : 0 ) + ( v3Out ? 1 : 0 );
 
-						// one vertex lies outside of the plane, perform clipping
+			switch ( total ) {
 
-						if ( v1Out ) {
+				case 0: {
 
-							nV1 = inVertices[ i + 1 ];
-							nV2 = inVertices[ i + 2 ];
-							nV3 = clip( inVertices[ i ], nV1, plane, s );
-							nV4 = clip( inVertices[ i ], nV2, plane, s );
+					// the entire face lies inside of the plane, no clipping needed
 
-						}
+					outVertices.push( inVertices[ i ] );
+					outVertices.push( inVertices[ i + 1 ] );
+					outVertices.push( inVertices[ i + 2 ] );
+					break;
 
-						if ( v2Out ) {
+				}
 
-							nV1 = inVertices[ i ];
-							nV2 = inVertices[ i + 2 ];
-							nV3 = clip( inVertices[ i + 1 ], nV1, plane, s );
-							nV4 = clip( inVertices[ i + 1 ], nV2, plane, s );
+				case 1: {
 
-							outVertices.push( nV3 );
-							outVertices.push( nV2.clone() );
-							outVertices.push( nV1.clone() );
+					// one vertex lies outside of the plane, perform clipping
 
-							outVertices.push( nV2.clone() );
-							outVertices.push( nV3.clone() );
-							outVertices.push( nV4 );
-							break;
+					if ( v1Out ) {
 
-						}
+						nV1 = inVertices[ i + 1 ];
+						nV2 = inVertices[ i + 2 ];
+						nV3 = clip( inVertices[ i ], nV1, plane, s );
+						nV4 = clip( inVertices[ i ], nV2, plane, s );
 
-						if ( v3Out ) {
+					}
 
-							nV1 = inVertices[ i ];
-							nV2 = inVertices[ i + 1 ];
-							nV3 = clip( inVertices[ i + 2 ], nV1, plane, s );
-							nV4 = clip( inVertices[ i + 2 ], nV2, plane, s );
+					if ( v2Out ) {
 
-						}
+						nV1 = inVertices[ i ];
+						nV2 = inVertices[ i + 2 ];
+						nV3 = clip( inVertices[ i + 1 ], nV1, plane, s );
+						nV4 = clip( inVertices[ i + 1 ], nV2, plane, s );
 
-						outVertices.push( nV1.clone() );
-						outVertices.push( nV2.clone() );
 						outVertices.push( nV3 );
-
-						outVertices.push( nV4 );
-						outVertices.push( nV3.clone() );
 						outVertices.push( nV2.clone() );
+						outVertices.push( nV1.clone() );
 
+						outVertices.push( nV2.clone() );
+						outVertices.push( nV3.clone() );
+						outVertices.push( nV4 );
 						break;
 
 					}
 
-					case 2: {
+					if ( v3Out ) {
 
-						// two vertices lies outside of the plane, perform clipping
+						nV1 = inVertices[ i ];
+						nV2 = inVertices[ i + 1 ];
+						nV3 = clip( inVertices[ i + 2 ], nV1, plane, s );
+						nV4 = clip( inVertices[ i + 2 ], nV2, plane, s );
 
-						if ( ! v1Out ) {
-
-							nV1 = inVertices[ i ].clone();
-							nV2 = clip( nV1, inVertices[ i + 1 ], plane, s );
-							nV3 = clip( nV1, inVertices[ i + 2 ], plane, s );
-							outVertices.push( nV1 );
-							outVertices.push( nV2 );
-							outVertices.push( nV3 );
+					}
 
-						}
+					outVertices.push( nV1.clone() );
+					outVertices.push( nV2.clone() );
+					outVertices.push( nV3 );
 
-						if ( ! v2Out ) {
+					outVertices.push( nV4 );
+					outVertices.push( nV3.clone() );
+					outVertices.push( nV2.clone() );
 
-							nV1 = inVertices[ i + 1 ].clone();
-							nV2 = clip( nV1, inVertices[ i + 2 ], plane, s );
-							nV3 = clip( nV1, inVertices[ i ], plane, s );
-							outVertices.push( nV1 );
-							outVertices.push( nV2 );
-							outVertices.push( nV3 );
+					break;
 
-						}
+				}
 
-						if ( ! v3Out ) {
+				case 2: {
 
-							nV1 = inVertices[ i + 2 ].clone();
-							nV2 = clip( nV1, inVertices[ i ], plane, s );
-							nV3 = clip( nV1, inVertices[ i + 1 ], plane, s );
-							outVertices.push( nV1 );
-							outVertices.push( nV2 );
-							outVertices.push( nV3 );
+					// two vertices lies outside of the plane, perform clipping
 
-						}
+					if ( ! v1Out ) {
 
-						break;
+						nV1 = inVertices[ i ].clone();
+						nV2 = clip( nV1, inVertices[ i + 1 ], plane, s );
+						nV3 = clip( nV1, inVertices[ i + 2 ], plane, s );
+						outVertices.push( nV1 );
+						outVertices.push( nV2 );
+						outVertices.push( nV3 );
 
 					}
 
-					case 3: {
+					if ( ! v2Out ) {
 
-						// the entire face lies outside of the plane, so let's discard the corresponding vertices
-
-						break;
+						nV1 = inVertices[ i + 1 ].clone();
+						nV2 = clip( nV1, inVertices[ i + 2 ], plane, s );
+						nV3 = clip( nV1, inVertices[ i ], plane, s );
+						outVertices.push( nV1 );
+						outVertices.push( nV2 );
+						outVertices.push( nV3 );
 
 					}
 
-				}
+					if ( ! v3Out ) {
 
-			}
+						nV1 = inVertices[ i + 2 ].clone();
+						nV2 = clip( nV1, inVertices[ i ], plane, s );
+						nV3 = clip( nV1, inVertices[ i + 1 ], plane, s );
+						outVertices.push( nV1 );
+						outVertices.push( nV2 );
+						outVertices.push( nV3 );
 
-			return outVertices;
+					}
 
-		}
+					break;
 
-		function clip( v0, v1, p, s ) {
+				}
 
-			var d0 = v0.position.dot( p ) - s;
-			var d1 = v1.position.dot( p ) - s;
+				case 3: {
 
-			var s0 = d0 / ( d0 - d1 );
+					// the entire face lies outside of the plane, so let's discard the corresponding vertices
 
-			var v = new DecalVertex(
-				new THREE.Vector3(
-					v0.position.x + s0 * ( v1.position.x - v0.position.x ),
-					v0.position.y + s0 * ( v1.position.y - v0.position.y ),
-					v0.position.z + s0 * ( v1.position.z - v0.position.z )
-				),
-				new THREE.Vector3(
-					v0.normal.x + s0 * ( v1.normal.x - v0.normal.x ),
-					v0.normal.y + s0 * ( v1.normal.y - v0.normal.y ),
-					v0.normal.z + s0 * ( v1.normal.z - v0.normal.z )
-				)
-			);
+					break;
 
-			// need to clip more values (texture coordinates)? do it this way:
-			// intersectpoint.value = a.value + s * ( b.value - a.value );
+				}
 
-			return v;
+			}
 
 		}
 
+		return outVertices;
+
 	}
 
-	DecalGeometry.prototype = Object.create( THREE.BufferGeometry.prototype );
-	DecalGeometry.prototype.constructor = DecalGeometry;
+	function clip( v0, v1, p, s ) {
+
+		var d0 = v0.position.dot( p ) - s;
+		var d1 = v1.position.dot( p ) - s;
+
+		var s0 = d0 / ( d0 - d1 );
 
-	// helper
+		var v = new THREE.DecalVertex(
+			new THREE.Vector3(
+				v0.position.x + s0 * ( v1.position.x - v0.position.x ),
+				v0.position.y + s0 * ( v1.position.y - v0.position.y ),
+				v0.position.z + s0 * ( v1.position.z - v0.position.z )
+			),
+			new THREE.Vector3(
+				v0.normal.x + s0 * ( v1.normal.x - v0.normal.x ),
+				v0.normal.y + s0 * ( v1.normal.y - v0.normal.y ),
+				v0.normal.z + s0 * ( v1.normal.z - v0.normal.z )
+			)
+		);
 
-	function DecalVertex( position, normal ) {
+		// need to clip more values (texture coordinates)? do it this way:
+		// intersectpoint.value = a.value + s * ( b.value - a.value );
 
-		this.position = position;
-		this.normal = normal;
+		return v;
 
 	}
 
-	DecalVertex.prototype.clone = function () {
+};
+
+THREE.DecalGeometry.prototype = Object.create( THREE.BufferGeometry.prototype );
+THREE.DecalGeometry.prototype.constructor = THREE.DecalGeometry;
+
+// helper
+
+THREE.DecalVertex = function ( position, normal ) {
 
-		return new DecalVertex( this.position.clone(), this.normal.clone() );
+	this.position = position;
+	this.normal = normal;
 
-	};
+};
 
-	// export
+THREE.DecalVertex.prototype.clone = function () {
 
-	THREE.DecalGeometry = DecalGeometry;
+	return new this.constructor( this.position.clone(), this.normal.clone() );
 
-} )();
+};

+ 0 - 157
examples/js/geometries/Hilbert.js

@@ -1,157 +0,0 @@
-/**
- * Hilbert Curves.
- *
- * @author Dylan Grafmyre
- */
-
-THREE.Hilbert = {
-
-	/**
-	 * Generates 2D-Coordinates in a very fast way.
-	 *
-	 * @author Dylan Grafmyre
-	 *
-	 * Based on work by:
-	 * @author Thomas Diewald
-	 * @link http://www.openprocessing.org/sketch/15493
-	 *
-	 * @param center     Center of Hilbert curve.
-	 * @param size       Total width of Hilbert curve.
-	 * @param iterations Number of subdivisions.
-	 * @param v0         Corner index -X, -Z.
-	 * @param v1         Corner index -X, +Z.
-	 * @param v2         Corner index +X, +Z.
-	 * @param v3         Corner index +X, -Z.
-	 */
-	generate2D( center, size, iterations, v0, v1, v2, v3 ) {
-
-		// Default Vars
-		var center = center !== undefined ? center : new THREE.Vector3( 0, 0, 0 ),
-			size = size !== undefined ? size : 10,
-			half = size / 2,
-			iterations = iterations !== undefined ? iterations : 1,
-			v0 = v0 !== undefined ? v0 : 0,
-			v1 = v1 !== undefined ? v1 : 1,
-			v2 = v2 !== undefined ? v2 : 2,
-			v3 = v3 !== undefined ? v3 : 3
-		;
-
-		var vec_s = [
-			new THREE.Vector3( center.x - half, center.y, center.z - half ),
-			new THREE.Vector3( center.x - half, center.y, center.z + half ),
-			new THREE.Vector3( center.x + half, center.y, center.z + half ),
-			new THREE.Vector3( center.x + half, center.y, center.z - half )
-		];
-
-		var vec = [
-			vec_s[ v0 ],
-			vec_s[ v1 ],
-			vec_s[ v2 ],
-			vec_s[ v3 ]
-		];
-
-		// Recurse iterations
-		if ( 0 <= -- iterations ) {
-
-			var tmp = [];
-
-			Array.prototype.push.apply( tmp, THREE.Hilbert.generate2D( vec[ 0 ], half, iterations, v0, v3, v2, v1 ) );
-			Array.prototype.push.apply( tmp, THREE.Hilbert.generate2D( vec[ 1 ], half, iterations, v0, v1, v2, v3 ) );
-			Array.prototype.push.apply( tmp, THREE.Hilbert.generate2D( vec[ 2 ], half, iterations, v0, v1, v2, v3 ) );
-			Array.prototype.push.apply( tmp, THREE.Hilbert.generate2D( vec[ 3 ], half, iterations, v2, v1, v0, v3 ) );
-
-			// Return recursive call
-			return tmp;
-
-		}
-
-		// Return complete Hilbert Curve.
-		return vec;
-
-	},
-
-	/**
-	 * Generates 3D-Coordinates in a very fast way.
-	 *
-	 * @author Dylan Grafmyre
-	 *
-	 * Based on work by:
-	 * @author Thomas Diewald
-	 * @link http://www.openprocessing.org/visuals/?visualID=15599
-	 *
-	 * @param center     Center of Hilbert curve.
-	 * @param size       Total width of Hilbert curve.
-	 * @param iterations Number of subdivisions.
-	 * @param v0         Corner index -X, +Y, -Z.
-	 * @param v1         Corner index -X, +Y, +Z.
-	 * @param v2         Corner index -X, -Y, +Z.
-	 * @param v3         Corner index -X, -Y, -Z.
-	 * @param v4         Corner index +X, -Y, -Z.
-	 * @param v5         Corner index +X, -Y, +Z.
-	 * @param v6         Corner index +X, +Y, +Z.
-	 * @param v7         Corner index +X, +Y, -Z.
-	 */
-	generate3D( center, size, iterations, v0, v1, v2, v3, v4, v5, v6, v7 ) {
-
-		// Default Vars
-		var center = center !== undefined ? center : new THREE.Vector3( 0, 0, 0 ),
-			size = size !== undefined ? size : 10,
-			half = size / 2,
-			iterations = iterations !== undefined ? iterations : 1,
-			v0 = v0 !== undefined ? v0 : 0,
-			v1 = v1 !== undefined ? v1 : 1,
-			v2 = v2 !== undefined ? v2 : 2,
-			v3 = v3 !== undefined ? v3 : 3,
-			v4 = v4 !== undefined ? v4 : 4,
-			v5 = v5 !== undefined ? v5 : 5,
-			v6 = v6 !== undefined ? v6 : 6,
-			v7 = v7 !== undefined ? v7 : 7
-		;
-
-		var vec_s = [
-			new THREE.Vector3( center.x - half, center.y + half, center.z - half ),
-			new THREE.Vector3( center.x - half, center.y + half, center.z + half ),
-			new THREE.Vector3( center.x - half, center.y - half, center.z + half ),
-			new THREE.Vector3( center.x - half, center.y - half, center.z - half ),
-			new THREE.Vector3( center.x + half, center.y - half, center.z - half ),
-			new THREE.Vector3( center.x + half, center.y - half, center.z + half ),
-			new THREE.Vector3( center.x + half, center.y + half, center.z + half ),
-			new THREE.Vector3( center.x + half, center.y + half, center.z - half )
-		];
-
-		var vec = [
-			vec_s[ v0 ],
-			vec_s[ v1 ],
-			vec_s[ v2 ],
-			vec_s[ v3 ],
-			vec_s[ v4 ],
-			vec_s[ v5 ],
-			vec_s[ v6 ],
-			vec_s[ v7 ]
-		];
-
-		// Recurse iterations
-		if ( -- iterations >= 0 ) {
-
-			var tmp = [];
-
-			Array.prototype.push.apply( tmp, THREE.Hilbert.generate3D( vec[ 0 ], half, iterations, v0, v3, v4, v7, v6, v5, v2, v1 ) );
-			Array.prototype.push.apply( tmp, THREE.Hilbert.generate3D( vec[ 1 ], half, iterations, v0, v7, v6, v1, v2, v5, v4, v3 ) );
-			Array.prototype.push.apply( tmp, THREE.Hilbert.generate3D( vec[ 2 ], half, iterations, v0, v7, v6, v1, v2, v5, v4, v3 ) );
-			Array.prototype.push.apply( tmp, THREE.Hilbert.generate3D( vec[ 3 ], half, iterations, v2, v3, v0, v1, v6, v7, v4, v5 ) );
-			Array.prototype.push.apply( tmp, THREE.Hilbert.generate3D( vec[ 4 ], half, iterations, v2, v3, v0, v1, v6, v7, v4, v5 ) );
-			Array.prototype.push.apply( tmp, THREE.Hilbert.generate3D( vec[ 5 ], half, iterations, v4, v3, v2, v5, v6, v1, v0, v7 ) );
-			Array.prototype.push.apply( tmp, THREE.Hilbert.generate3D( vec[ 6 ], half, iterations, v4, v3, v2, v5, v6, v1, v0, v7 ) );
-			Array.prototype.push.apply( tmp, THREE.Hilbert.generate3D( vec[ 7 ], half, iterations, v6, v5, v2, v1, v0, v3, v4, v7 ) );
-
-			// Return recursive call
-			return tmp;
-
-		}
-
-		// Return complete Hilbert Curve.
-		return vec;
-
-	}
-
-};

+ 3 - 3
examples/js/geometries/LightningStrike.js

@@ -381,9 +381,9 @@ THREE.LightningStrike.prototype.init = function ( rayParameters ) {
 	this.positionAttribute = null;
 	this.uvsAttribute = null;
 
-	this.simplexX = new SimplexNoise( this.seedGenerator );
-	this.simplexY = new SimplexNoise( this.seedGenerator );
-	this.simplexZ = new SimplexNoise( this.seedGenerator );
+	this.simplexX = new THREE.SimplexNoise( this.seedGenerator );
+	this.simplexY = new THREE.SimplexNoise( this.seedGenerator );
+	this.simplexZ = new THREE.SimplexNoise( this.seedGenerator );
 
 	// Temp vectors
 	this.forwards = new THREE.Vector3();

+ 0 - 0
examples/js/ParametricGeometries.js → examples/js/geometries/ParametricGeometries.js


+ 1 - 4
examples/js/geometries/TeapotBufferGeometry.js

@@ -3,9 +3,7 @@
  *
  * Tessellates the famous Utah teapot database by Martin Newell into triangles.
  *
- * THREE.TeapotBufferGeometry = function ( size, segments, bottom, lid, body, fitLid, blinn )
- *
- * defaults: size = 50, segments = 10, bottom = true, lid = true, body = true,
+ * Parameters: size = 50, segments = 10, bottom = true, lid = true, body = true,
  *   fitLid = false, blinn = true
  *
  * size is a relative scale: I've scaled the teapot to fit vertically between -1 and 1.
@@ -50,7 +48,6 @@
  * See https://en.wikipedia.org/wiki/Utah_teapot for the history of the teapot
  *
  */
-/*global THREE */
 
 THREE.TeapotBufferGeometry = function ( size, segments, bottom, lid, body, fitLid, blinn ) {
 

+ 4 - 4
examples/js/libs/basis/README.md

@@ -22,21 +22,21 @@ basisLoader.setTranscoderPath( 'examples/js/libs/basis/' );
 basisLoader.detectSupport( renderer );
 basisLoader.load( 'diffuse.basis', function ( texture ) {
 
-  var material = new THREE.MeshStandardMaterial( { map: texture } );
+	var material = new THREE.MeshStandardMaterial( { map: texture } );
 
 }, function () {
 
-  console.log( 'onProgress' );
+	console.log( 'onProgress' );
 
 }, function ( e ) {
 
-  console.error( e );
+	console.error( e );
 
 } );
 ```
 
 For further documentation about the Basis compressor and transcoder, refer to
-the [Basis GitHub repository](https://github.com/BinomialLLC/basis_universal).
+the [Basis GitHub repository](https://github.com/BinomialLLC/basis_universal). The JavaScript wrapper requires one modification from the version provided in the Basis repository – the declaration on the first line is changed from `var Module` to `Module`, to accomodate lazy initialization in a Web Worker ([details](https://github.com/mrdoob/three.js/issues/16524)).
 
 ## License
 

File diff suppressed because it is too large
+ 0 - 0
examples/js/libs/basis/basis_transcoder.js


BIN
examples/js/libs/basis/basis_transcoder.wasm


+ 1 - 1
examples/js/lines/Line2.js

@@ -20,7 +20,7 @@ THREE.Line2.prototype = Object.assign( Object.create( THREE.LineSegments2.protot
 
 	isLine2: true,
 
-	copy: function ( source ) {
+	copy: function ( /* source */ ) {
 
 		// todo
 

+ 1 - 1
examples/js/lines/LineGeometry.js

@@ -87,7 +87,7 @@ THREE.LineGeometry.prototype = Object.assign( Object.create( THREE.LineSegmentsG
 
 	},
 
-	copy: function ( source ) {
+	copy: function ( /* source */ ) {
 
 		// todo
 

+ 1 - 1
examples/js/lines/LineSegments2.js

@@ -54,7 +54,7 @@ THREE.LineSegments2.prototype = Object.assign( Object.create( THREE.Mesh.prototy
 
 	}() ),
 
-	copy: function ( source ) {
+	copy: function ( /* source */ ) {
 
 		// todo
 

+ 1 - 3
examples/js/lines/LineSegmentsGeometry.js

@@ -9,8 +9,6 @@ THREE.LineSegmentsGeometry = function () {
 
 	this.type = 'LineSegmentsGeometry';
 
-	var plane = new THREE.BufferGeometry();
-
 	var positions = [ - 1, 2, 0, 1, 2, 0, - 1, 1, 0, 1, 1, 0, - 1, 0, 0, 1, 0, 0, - 1, - 1, 0, 1, - 1, 0 ];
 	var uvs = [ - 1, 2, 1, 2, - 1, 1, 1, 1, - 1, - 1, 1, - 1, - 1, - 2, 1, - 2 ];
 	var index = [ 0, 2, 1, 2, 3, 1, 2, 4, 3, 4, 5, 3, 4, 6, 5, 6, 7, 5 ];
@@ -249,7 +247,7 @@ THREE.LineSegmentsGeometry.prototype = Object.assign( Object.create( THREE.Insta
 
 	},
 
-	copy: function ( source ) {
+	copy: function ( /* source */ ) {
 
 		// todo
 

+ 1 - 1
examples/js/lines/Wireframe.js

@@ -54,7 +54,7 @@ THREE.Wireframe.prototype = Object.assign( Object.create( THREE.Mesh.prototype )
 
 	}() ),
 
-	copy: function ( source ) {
+	copy: function ( /* source */ ) {
 
 		// todo
 

+ 1 - 1
examples/js/lines/WireframeGeometry2.js

@@ -21,7 +21,7 @@ THREE.WireframeGeometry2.prototype = Object.assign( Object.create( THREE.LineSeg
 
 	isWireframeGeometry2: true,
 
-	copy: function ( source ) {
+	copy: function ( /* source */ ) {
 
 		// todo
 

+ 27 - 132
examples/js/loaders/AssimpLoader.js

@@ -359,7 +359,7 @@ THREE.AssimpLoader.prototype = {
 		var ASSBIN_MESH_HAS_COLOR_BASE = 0x10000;
 		var AI_MAX_NUMBER_OF_COLOR_SETS = 1;
 		var AI_MAX_NUMBER_OF_TEXTURECOORDS = 4;
-		var aiLightSource_UNDEFINED = 0x0;
+		//var aiLightSource_UNDEFINED = 0x0;
 		//! A directional light source has a well-defined direction
 		//! but is infinitely far away. That's quite a good
 		//! approximation for sun light.
@@ -367,7 +367,7 @@ THREE.AssimpLoader.prototype = {
 		//! A point light source has a well-defined position
 		//! in space but no direction - it emits light in all
 		//! directions. A normal bulb is a point light.
-		var aiLightSource_POINT = 0x2;
+		//var aiLightSource_POINT = 0x2;
 		//! A spot light source emits light in a specific
 		//! angle. It has a position and a direction it is pointing to.
 		//! A good example for a spot light is a light spot in
@@ -378,49 +378,49 @@ THREE.AssimpLoader.prototype = {
 		//! Typically, there's at most one ambient light in a scene.
 		//! This light type doesn't have a valid position, direction, or
 		//! other properties, just a color.
-		var aiLightSource_AMBIENT = 0x4;
+		//var aiLightSource_AMBIENT = 0x4;
 		/** Flat shading. Shading is done on per-face base,
 		 *  diffuse only. Also known as 'faceted shading'.
 		 */
-		var aiShadingMode_Flat = 0x1;
+		//var aiShadingMode_Flat = 0x1;
 		/** Simple Gouraud shading.
 		 */
-		var aiShadingMode_Gouraud = 0x2;
+		//var aiShadingMode_Gouraud = 0x2;
 		/** Phong-Shading -
 		 */
-		var aiShadingMode_Phong = 0x3;
+		//var aiShadingMode_Phong = 0x3;
 		/** Phong-Blinn-Shading
 		 */
-		var aiShadingMode_Blinn = 0x4;
+		//var aiShadingMode_Blinn = 0x4;
 		/** Toon-Shading per pixel
 		 *
 		 *  Also known as 'comic' shader.
 		 */
-		var aiShadingMode_Toon = 0x5;
+		//var aiShadingMode_Toon = 0x5;
 		/** OrenNayar-Shading per pixel
 		 *
 		 *  Extension to standard Lambertian shading, taking the
 		 *  roughness of the material into account
 		 */
-		var aiShadingMode_OrenNayar = 0x6;
+		//var aiShadingMode_OrenNayar = 0x6;
 		/** Minnaert-Shading per pixel
 		 *
 		 *  Extension to standard Lambertian shading, taking the
 		 *  "darkness" of the material into account
 		 */
-		var aiShadingMode_Minnaert = 0x7;
+		//var aiShadingMode_Minnaert = 0x7;
 		/** CookTorrance-Shading per pixel
 		 *
 		 *  Special shader for metallic surfaces.
 		 */
-		var aiShadingMode_CookTorrance = 0x8;
+		//var aiShadingMode_CookTorrance = 0x8;
 		/** No shading at all. Constant light influence of 1.0.
 		 */
-		var aiShadingMode_NoShading = 0x9;
+		//var aiShadingMode_NoShading = 0x9;
 		/** Fresnel shading
 		 */
-		var aiShadingMode_Fresnel = 0xa;
-		var aiTextureType_NONE = 0x0;
+		//var aiShadingMode_Fresnel = 0xa;
+		//var aiTextureType_NONE = 0x0;
 		/** The texture is combined with the result of the diffuse
 		 *  lighting equation.
 		 */
@@ -428,21 +428,21 @@ THREE.AssimpLoader.prototype = {
 		/** The texture is combined with the result of the specular
 		 *  lighting equation.
 		 */
-		var aiTextureType_SPECULAR = 0x2;
+		//var aiTextureType_SPECULAR = 0x2;
 		/** The texture is combined with the result of the ambient
 		 *  lighting equation.
 		 */
-		var aiTextureType_AMBIENT = 0x3;
+		//var aiTextureType_AMBIENT = 0x3;
 		/** The texture is added to the result of the lighting
 		 *  calculation. It isn't influenced by incoming light.
 		 */
-		var aiTextureType_EMISSIVE = 0x4;
+		//var aiTextureType_EMISSIVE = 0x4;
 		/** The texture is a height map.
 		 *
 		 *  By convention, higher gray-scale values stand for
 		 *  higher elevations from the base height.
 		 */
-		var aiTextureType_HEIGHT = 0x5;
+		//var aiTextureType_HEIGHT = 0x5;
 		/** The texture is a (tangent space) normal-map.
 		 *
 		 *  Again, there are several conventions for tangent-space
@@ -457,7 +457,7 @@ THREE.AssimpLoader.prototype = {
 		 *  function defined to map the linear color values in the
 		 *  texture to a suitable exponent. Have fun.
 		 */
-		var aiTextureType_SHININESS = 0x7;
+		//var aiTextureType_SHININESS = 0x7;
 		/** The texture defines per-pixel opacity.
 		 *
 		 *  Usually 'white' means opaque and 'black' means
@@ -469,7 +469,7 @@ THREE.AssimpLoader.prototype = {
 		 *  The exact purpose and format is application-dependent.
 		 *  Higher color values stand for higher vertex displacements.
 		 */
-		var aiTextureType_DISPLACEMENT = 0x9;
+		//var aiTextureType_DISPLACEMENT = 0x9;
 		/** Lightmap texture (aka Ambient Occlusion)
 		 *
 		 *  Both 'Lightmaps' and dedicated 'ambient occlusion maps' are
@@ -483,14 +483,14 @@ THREE.AssimpLoader.prototype = {
 		 * Contains the color of a perfect mirror reflection.
 		 * Rarely used, almost never for real-time applications.
 		 */
-		var aiTextureType_REFLECTION = 0xB;
+		//var aiTextureType_REFLECTION = 0xB;
 		/** Unknown texture
 		 *
 		 *  A texture reference that does not match any of the definitions
 		 *  above is considered to be 'unknown'. It is still imported,
 		 *  but is excluded from any further postprocessing.
 		 */
-		var aiTextureType_UNKNOWN = 0xC;
+		//var aiTextureType_UNKNOWN = 0xC;
 		var BONESPERVERT = 4;
 
 		function ASSBIN_MESH_HAS_TEXCOORD( n ) {
@@ -634,7 +634,7 @@ THREE.AssimpLoader.prototype = {
 			];
 			this.mFaces = [];
 			this.mBones = [];
-			this.hookupSkeletons = function ( scene, threeScene ) {
+			this.hookupSkeletons = function ( scene ) {
 
 				if ( this.mBones.length == 0 ) return;
 
@@ -668,7 +668,6 @@ THREE.AssimpLoader.prototype = {
 						var skeletonRoot = scene.findNode( this.mBones[ i ].mName );
 						if ( ! skeletonRoot ) return;
 						var threeSkeletonRoot = skeletonRoot.toTHREE( scene );
-						var threeSkeletonRootParent = threeSkeletonRoot.parent;
 						var threeSkeletonRootBone = cloneTreeToBones( threeSkeletonRoot, scene );
 						this.threeNode.add( threeSkeletonRootBone );
 						var bone = findMatchingBone( threeSkeletonRootBone, this.mBones[ i ].mName );
@@ -808,46 +807,6 @@ THREE.AssimpLoader.prototype = {
 
 		}
 
-		function aiVector2D() {
-
-			this.x = 0;
-			this.y = 0;
-			this.toTHREE = function () {
-
-				return new THREE.Vector2( this.x, this.y );
-
-			};
-
-		}
-
-		function aiVector4D() {
-
-			this.w = 0;
-			this.x = 0;
-			this.y = 0;
-			this.z = 0;
-			this.toTHREE = function () {
-
-				return new THREE.Vector4( this.w, this.x, this.y, this.z );
-
-			};
-
-		}
-
-		function aiColor4D() {
-
-			this.r = 0;
-			this.g = 0;
-			this.b = 0;
-			this.a = 0;
-			this.toTHREE = function () {
-
-				return new THREE.Color( this.r, this.g, this.b, this.a );
-
-			};
-
-		}
-
 		function aiColor3D() {
 
 			this.r = 0;
@@ -856,7 +815,7 @@ THREE.AssimpLoader.prototype = {
 			this.a = 0;
 			this.toTHREE = function () {
 
-				return new THREE.Color( this.r, this.g, this.b, 1 );
+				return new THREE.Color( this.r, this.g, this.b );
 
 			};
 
@@ -1065,9 +1024,8 @@ THREE.AssimpLoader.prototype = {
 			this.mNumAllocated = 0;
 			this.mNumProperties = 0;
 			this.mProperties = [];
-			this.toTHREE = function ( scene ) {
+			this.toTHREE = function () {
 
-				var name = this.mProperties[ 0 ].dataAsString();
 				var mat = new THREE.MeshPhongMaterial();
 
 				for ( var i = 0; i < this.mProperties.length; i ++ ) {
@@ -1238,7 +1196,7 @@ THREE.AssimpLoader.prototype = {
 
 			};
 
-			this.toTHREE = function ( o, tps ) {
+			this.toTHREE = function ( o ) {
 
 				this.sortKeys();
 				var length = this.getLength();
@@ -1404,7 +1362,7 @@ THREE.AssimpLoader.prototype = {
 				var o = this.mRootNode.toTHREE( this );
 
 				for ( var i in this.mMeshes )
-					this.mMeshes[ i ].hookupSkeletons( this, o );
+					this.mMeshes[ i ].hookupSkeletons( this );
 
 				if ( this.mAnimations.length > 0 ) {
 
@@ -1506,26 +1464,6 @@ THREE.AssimpLoader.prototype = {
 
 		}
 
-		function Read_aiVector2D( stream ) {
-
-			var v = new aiVector2D();
-			v.x = readFloat( stream );
-			v.y = readFloat( stream );
-			return v;
-
-		}
-
-		function Read_aiVector4D( stream ) {
-
-			var v = new aiVector4D();
-			v.w = readFloat( stream );
-			v.x = readFloat( stream );
-			v.y = readFloat( stream );
-			v.z = readFloat( stream );
-			return v;
-
-		}
-
 		function Read_aiColor3D( stream ) {
 
 			var c = new aiColor3D();
@@ -1536,17 +1474,6 @@ THREE.AssimpLoader.prototype = {
 
 		}
 
-		function Read_aiColor4D( stream ) {
-
-			var c = new aiColor4D();
-			c.r = readFloat( stream );
-			c.g = readFloat( stream );
-			c.b = readFloat( stream );
-			c.a = readFloat( stream );
-			return c;
-
-		}
-
 		function Read_aiQuaternion( stream ) {
 
 			var v = new aiQuaternion();
@@ -1612,42 +1539,12 @@ THREE.AssimpLoader.prototype = {
 
 		}
 
-		function ReadArray( stream, data, size ) {
-
-			for ( var i = 0; i < size; i ++ ) data[ i ] = Read( stream );
-
-		}
-
-		function ReadArray_aiVector2D( stream, data, size ) {
-
-			for ( var i = 0; i < size; i ++ ) data[ i ] = Read_aiVector2D( stream );
-
-		}
-
-		function ReadArray_aiVector3D( stream, data, size ) {
-
-			for ( var i = 0; i < size; i ++ ) data[ i ] = Read_aiVector3D( stream );
-
-		}
-
-		function ReadArray_aiVector4D( stream, data, size ) {
-
-			for ( var i = 0; i < size; i ++ ) data[ i ] = Read_aiVector4D( stream );
-
-		}
-
 		function ReadArray_aiVertexWeight( stream, data, size ) {
 
 			for ( var i = 0; i < size; i ++ ) data[ i ] = Read_aiVertexWeight( stream );
 
 		}
 
-		function ReadArray_aiColor4D( stream, data, size ) {
-
-			for ( var i = 0; i < size; i ++ ) data[ i ] = Read_aiColor4D( stream );
-
-		}
-
 		function ReadArray_aiVectorKey( stream, data, size ) {
 
 			for ( var i = 0; i < size; i ++ ) data[ i ] = Read_aiVectorKey( stream );
@@ -1886,8 +1783,6 @@ THREE.AssimpLoader.prototype = {
 
 				// if there are less than 2^16 vertices, we can simply use 16 bit integers ...
 				mesh.mFaces = [];
-
-				var indexCounter = 0;
 				mesh.mIndexArray = [];
 
 				for ( var i = 0; i < mesh.mNumFaces; ++ i ) {

+ 174 - 90
examples/js/loaders/BasisTextureLoader.js

@@ -16,46 +16,66 @@
  * of web workers, before transferring the transcoded compressed texture back
  * to the main thread.
  */
-// TODO(donmccurdy): Don't use ES6 classes.
-THREE.BasisTextureLoader = class BasisTextureLoader {
+THREE.BasisTextureLoader = function ( manager ) {
 
-	constructor ( manager ) {
+	this.manager = manager || THREE.DefaultLoadingManager;
 
-		// TODO(donmccurdy): Loading manager is unused.
-		this.manager = manager || THREE.DefaultLoadingManager;
+	this.crossOrigin = 'anonymous';
 
-		this.transcoderPath = '';
-		this.transcoderBinary = null;
-		this.transcoderPending = null;
+	this.transcoderPath = '';
+	this.transcoderBinary = null;
+	this.transcoderPending = null;
 
-		this.workerLimit = 4;
-		this.workerPool = [];
-		this.workerNextTaskID = 1;
-		this.workerSourceURL = '';
-		this.workerConfig = {
-			format: null,
-			etcSupported: false,
-			dxtSupported: false,
-			pvrtcSupported: false,
-		};
+	this.workerLimit = 4;
+	this.workerPool = [];
+	this.workerNextTaskID = 1;
+	this.workerSourceURL = '';
+	this.workerConfig = {
+		format: null,
+		etcSupported: false,
+		dxtSupported: false,
+		pvrtcSupported: false,
+	};
 
-	}
+};
+
+THREE.BasisTextureLoader.prototype = {
+
+	constructor: THREE.BasisTextureLoader,
+
+	setCrossOrigin: function ( crossOrigin ) {
+
+		this.crossOrigin = crossOrigin;
+
+		return this;
 
-	setTranscoderPath ( path ) {
+	},
+
+	setTranscoderPath: function ( path ) {
 
 		this.transcoderPath = path;
 
-	}
+		return this;
+
+	},
+
+	setWorkerLimit: function ( workerLimit ) {
+
+		this.workerLimit = workerLimit;
+
+		return this;
 
-	detectSupport ( renderer ) {
+	},
+
+	detectSupport: function ( renderer ) {
 
 		var context = renderer.context;
 		var config = this.workerConfig;
 
-		config.etcSupported = !! context.getExtension('WEBGL_compressed_texture_etc1');
-		config.dxtSupported = !! context.getExtension('WEBGL_compressed_texture_s3tc');
-		config.pvrtcSupported = !! context.getExtension('WEBGL_compressed_texture_pvrtc')
-			|| !! context.getExtension('WEBKIT_WEBGL_compressed_texture_pvrtc');
+		config.etcSupported = !! context.getExtension( 'WEBGL_compressed_texture_etc1' );
+		config.dxtSupported = !! context.getExtension( 'WEBGL_compressed_texture_s3tc' );
+		config.pvrtcSupported = !! context.getExtension( 'WEBGL_compressed_texture_pvrtc' )
+			|| !! context.getExtension( 'WEBKIT_WEBGL_compressed_texture_pvrtc' );
 
 		if ( config.etcSupported ) {
 
@@ -77,36 +97,44 @@ THREE.BasisTextureLoader = class BasisTextureLoader {
 
 		return this;
 
-	}
+	},
 
-	load ( url, onLoad, onProgress, onError ) {
+	load: function ( url, onLoad, onProgress, onError ) {
 
-		// TODO(donmccurdy): Use THREE.FileLoader.
-		fetch( url )
-			.then( ( res ) => res.arrayBuffer() )
-			.then( ( buffer ) => this._createTexture( buffer ) )
-			.then( onLoad )
-			.catch( onError );
+		var loader = new THREE.FileLoader( this.manager );
 
-	}
+		loader.setResponseType( 'arraybuffer' );
+
+		loader.load( url, ( buffer ) => {
+
+			this._createTexture( buffer )
+				.then( onLoad )
+				.catch( onError );
+
+		}, onProgress, onError );
+
+	},
 
 	/**
 	 * @param  {ArrayBuffer} buffer
 	 * @return {Promise<THREE.CompressedTexture>}
 	 */
-	_createTexture ( buffer ) {
+	_createTexture: function ( buffer ) {
+
+		var worker;
+		var taskID;
 
-		return this.getWorker()
-			.then( ( worker ) => {
+		var texturePending = this._getWorker()
+			.then( ( _worker ) => {
 
-				return new Promise( ( resolve ) => {
+				worker = _worker;
+				taskID = this.workerNextTaskID ++;
 
-					var taskID = this.workerNextTaskID++;
+				return new Promise( ( resolve, reject ) => {
 
-					worker._callbacks[ taskID ] = resolve;
+					worker._callbacks[ taskID ] = { resolve, reject };
 					worker._taskCosts[ taskID ] = buffer.byteLength;
 					worker._taskLoad += worker._taskCosts[ taskID ];
-					worker._taskCount++;
 
 					worker.postMessage( { type: 'transcode', id: taskID, buffer }, [ buffer ] );
 
@@ -117,9 +145,7 @@ THREE.BasisTextureLoader = class BasisTextureLoader {
 
 				var config = this.workerConfig;
 
-				var { data, width, height } = message;
-
-				var mipmaps = [ { data, width, height } ];
+				var { width, height, mipmaps } = message;
 
 				var texture;
 
@@ -143,27 +169,52 @@ THREE.BasisTextureLoader = class BasisTextureLoader {
 
 				texture.minFilter = THREE.LinearMipMapLinearFilter;
 				texture.magFilter = THREE.LinearFilter;
-				texture.encoding = THREE.sRGBEncoding;
 				texture.generateMipmaps = false;
-				texture.flipY = false;
 				texture.needsUpdate = true;
 
 				return texture;
 
-			});
+			} );
 
-	}
+		texturePending
+			.finally( () => {
+
+				if ( worker && taskID ) {
+
+					worker._taskLoad -= worker._taskCosts[ taskID ];
+					delete worker._callbacks[ taskID ];
+					delete worker._taskCosts[ taskID ];
+
+				}
+
+			} );
+
+		return texturePending;
 
-	_initTranscoder () {
+	},
+
+	_initTranscoder: function () {
 
 		if ( ! this.transcoderBinary ) {
 
-			// TODO(donmccurdy): Use THREE.FileLoader.
-			var jsContent = fetch( this.transcoderPath + 'basis_transcoder.js' )
-				.then( ( response ) => response.text() );
+			// Load transcoder wrapper.
+			var jsLoader = new THREE.FileLoader( this.manager );
+			jsLoader.setPath( this.transcoderPath );
+			var jsContent = new Promise( ( resolve, reject ) => {
+
+				jsLoader.load( 'basis_transcoder.js', resolve, undefined, reject );
+
+			} );
+
+			// Load transcoder WASM binary.
+			var binaryLoader = new THREE.FileLoader( this.manager );
+			binaryLoader.setPath( this.transcoderPath );
+			binaryLoader.setResponseType( 'arraybuffer' );
+			var binaryContent = new Promise( ( resolve, reject ) => {
+
+				binaryLoader.load( 'basis_transcoder.wasm', resolve, undefined, reject );
 
-			var binaryContent = fetch( this.transcoderPath + 'basis_transcoder.wasm' )
-				.then( ( response ) => response.arrayBuffer() );
+			} );
 
 			this.transcoderPending = Promise.all( [ jsContent, binaryContent ] )
 				.then( ( [ jsContent, binaryContent ] ) => {
@@ -191,9 +242,9 @@ THREE.BasisTextureLoader = class BasisTextureLoader {
 
 		return this.transcoderPending;
 
-	}
+	},
 
-	getWorker () {
+	_getWorker: function () {
 
 		return this._initTranscoder().then( () => {
 
@@ -204,7 +255,6 @@ THREE.BasisTextureLoader = class BasisTextureLoader {
 				worker._callbacks = {};
 				worker._taskCosts = {};
 				worker._taskLoad = 0;
-				worker._taskCount = 0;
 
 				worker.postMessage( {
 					type: 'init',
@@ -219,24 +269,29 @@ THREE.BasisTextureLoader = class BasisTextureLoader {
 					switch ( message.type ) {
 
 						case 'transcode':
-							worker._callbacks[ message.id ]( message );
-							worker._taskLoad -= worker._taskCosts[ message.id ];
-							delete worker._callbacks[ message.id ];
-							delete worker._taskCosts[ message.id ];
+							worker._callbacks[ message.id ].resolve( message );
+							break;
+
+						case 'error':
+							worker._callbacks[ message.id ].reject( message );
 							break;
 
 						default:
-							throw new Error( 'THREE.BasisTextureLoader: Unexpected message, "' + message.type + '"' );
+							console.error( 'THREE.BasisTextureLoader: Unexpected message, "' + message.type + '"' );
 
 					}
 
-				}
+				};
 
 				this.workerPool.push( worker );
 
 			} else {
 
-				this.workerPool.sort( function ( a, b ) { return a._taskLoad > b._taskLoad ? -1 : 1; } );
+				this.workerPool.sort( function ( a, b ) {
+
+					return a._taskLoad > b._taskLoad ? - 1 : 1;
+
+				} );
 
 			}
 
@@ -244,11 +299,11 @@ THREE.BasisTextureLoader = class BasisTextureLoader {
 
 		} );
 
-	}
+	},
 
-	dispose () {
+	dispose: function () {
 
-		for ( var i = 0; i < this.workerPool.length; i++ ) {
+		for ( var i = 0; i < this.workerPool.length; i ++ ) {
 
 			this.workerPool[ i ].terminate();
 
@@ -256,8 +311,10 @@ THREE.BasisTextureLoader = class BasisTextureLoader {
 
 		this.workerPool.length = 0;
 
+		return this;
+
 	}
-}
+};
 
 /* CONSTANTS */
 
@@ -289,6 +346,7 @@ THREE.BasisTextureLoader.DXT_FORMAT_MAP[ THREE.BasisTextureLoader.BASIS_FORMAT.c
 /* WEB WORKER */
 
 THREE.BasisTextureLoader.BasisWorker = function () {
+
 	var config;
 	var transcoderPending;
 	var _BasisFile;
@@ -307,9 +365,27 @@ THREE.BasisTextureLoader.BasisWorker = function () {
 			case 'transcode':
 				transcoderPending.then( () => {
 
-					var { data, width, height } = transcode( message.buffer );
+					try {
 
-					self.postMessage( { type: 'transcode', id: message.id, data, width, height }, [ data.buffer ] );
+						var { width, height, mipmaps } = transcode( message.buffer );
+
+						var buffers = [];
+
+						for ( var i = 0; i < mipmaps.length; ++ i ) {
+
+							buffers.push( mipmaps[ i ].data.buffer );
+
+						}
+
+						self.postMessage( { type: 'transcode', id: message.id, width, height, mipmaps }, buffers );
+
+					} catch ( error ) {
+
+						console.error( error );
+
+						self.postMessage( { type: 'error', id: message.id, error: error.message } );
+
+					}
 
 				} );
 				break;
@@ -318,7 +394,7 @@ THREE.BasisTextureLoader.BasisWorker = function () {
 
 	};
 
-	function init ( wasmBinary ) {
+	function init( wasmBinary ) {
 
 		transcoderPending = new Promise( ( resolve ) => {
 
@@ -344,23 +420,22 @@ THREE.BasisTextureLoader.BasisWorker = function () {
 
 	}
 
-	function transcode ( buffer ) {
+	function transcode( buffer ) {
 
 		var basisFile = new _BasisFile( new Uint8Array( buffer ) );
 
 		var width = basisFile.getImageWidth( 0, 0 );
 		var height = basisFile.getImageHeight( 0, 0 );
-		var images = basisFile.getNumImages();
 		var levels = basisFile.getNumLevels( 0 );
 
-		function cleanup () {
+		function cleanup() {
 
 			basisFile.close();
 			basisFile.delete();
 
 		}
 
-		if ( ! width || ! height || ! images || ! levels ) {
+		if ( ! width || ! height || ! levels ) {
 
 			cleanup();
 			throw new Error( 'THREE.BasisTextureLoader:  Invalid .basis file' );
@@ -374,28 +449,37 @@ THREE.BasisTextureLoader.BasisWorker = function () {
 
 		}
 
-		var dst = new Uint8Array( basisFile.getImageTranscodedSizeInBytes( 0, 0, config.format ) );
+		var mipmaps = [];
 
-		var startTime = performance.now();
+		for ( var mip = 0; mip < levels; mip ++ ) {
 
-		var status = basisFile.transcodeImage(
-			dst,
-			0,
-			0,
-			config.format,
-			config.etcSupported ? 0 : ( config.dxtSupported ? 1 : 0 ),
-			0
-		);
+			var mipWidth = basisFile.getImageWidth( 0, mip );
+			var mipHeight = basisFile.getImageHeight( 0, mip );
+			var dst = new Uint8Array( basisFile.getImageTranscodedSizeInBytes( 0, mip, config.format ) );
 
-		cleanup();
+			var status = basisFile.transcodeImage(
+				dst,
+				0,
+				mip,
+				config.format,
+				config.etcSupported ? 0 : ( config.dxtSupported ? 1 : 0 ),
+				0
+			);
+
+			if ( ! status ) {
 
-		if ( ! status ) {
+				cleanup();
+				throw new Error( 'THREE.BasisTextureLoader: .transcodeImage failed.' );
 
-			throw new Error( 'THREE.BasisTextureLoader: .transcodeImage failed.' );
+			}
+
+			mipmaps.push( { data: dst, width: mipWidth, height: mipHeight } );
 
 		}
 
-		return { data: dst, width, height };
+		cleanup();
+
+		return { width, height, mipmaps };
 
 	}
 

+ 10 - 21
examples/js/loaders/EXRLoader.js

@@ -96,7 +96,6 @@ THREE.EXRLoader.prototype._parser = function ( buffer ) {
 	const SHORT_ZEROCODE_RUN = 59;
 	const LONG_ZEROCODE_RUN = 63;
 	const SHORTEST_LONG_RUN = 2 + LONG_ZEROCODE_RUN - SHORT_ZEROCODE_RUN;
-	const LONGEST_LONG_RUN = 255 + SHORTEST_LONG_RUN;
 
 	const BYTES_PER_HALF = 2;
 
@@ -395,11 +394,6 @@ THREE.EXRLoader.prototype._parser = function ( buffer ) {
 
 	}
 
-	var NBITS = 16;
-	var A_OFFSET = 1 << ( NBITS - 1 );
-	var M_OFFSET = 1 << ( NBITS - 1 );
-	var MOD_MASK = ( 1 << NBITS ) - 1;
-
 	function UInt16( value ) {
 
 		return ( value & 0xFFFF );
@@ -431,7 +425,7 @@ THREE.EXRLoader.prototype._parser = function ( buffer ) {
 
 	}
 
-	function wav2Decode( j, buffer, nx, ox, ny, oy, mx ) {
+	function wav2Decode( j, buffer, nx, ox, ny, oy ) {
 
 		var n = ( nx > ny ) ? ny : nx;
 		var p = 1;
@@ -716,7 +710,7 @@ THREE.EXRLoader.prototype._parser = function ( buffer ) {
 		}
 
 		var lut = new Uint16Array( USHORT_RANGE );
-		var maxValue = reverseLutFromBitmap( bitmap, lut );
+		reverseLutFromBitmap( bitmap, lut );
 
 		var length = parseUint32( inDataView, inOffset );
 
@@ -728,10 +722,6 @@ THREE.EXRLoader.prototype._parser = function ( buffer ) {
 
 		for ( var i = 0; i < num_channels; i ++ ) {
 
-			var exrChannelInfo = exrChannelInfos[ i ];
-
-			var pixelSize = 2; // assumes HALF_FLOAT
-
 			pizChannelData[ i ] = {};
 			pizChannelData[ i ][ 'start' ] = outBufferEnd;
 			pizChannelData[ i ][ 'end' ] = pizChannelData[ i ][ 'start' ];
@@ -755,8 +745,7 @@ THREE.EXRLoader.prototype._parser = function ( buffer ) {
 					pizChannelData[ i ].nx,
 					pizChannelData[ i ].size,
 					pizChannelData[ i ].ny,
-					pizChannelData[ i ].nx * pizChannelData[ i ].size,
-					maxValue
+					pizChannelData[ i ].nx * pizChannelData[ i ].size
 				);
 
 			}
@@ -1035,9 +1024,9 @@ THREE.EXRLoader.prototype._parser = function ( buffer ) {
 
 	var EXRHeader = {};
 
-	var magic = bufferDataView.getUint32( 0, true );
-	var versionByteZero = bufferDataView.getUint8( 4, true );
-	var fullMask = bufferDataView.getUint8( 5, true );
+	bufferDataView.getUint32( 0, true ); // magic
+	bufferDataView.getUint8( 4, true ); // versionByteZero
+	bufferDataView.getUint8( 5, true ); // fullMask
 
 	// start of header
 
@@ -1080,7 +1069,7 @@ THREE.EXRLoader.prototype._parser = function ( buffer ) {
 
 	for ( var i = 0; i < numBlocks; i ++ ) {
 
-		var scanlineOffset = parseUlong( bufferDataView, offset );
+		parseUlong( bufferDataView, offset ); // scanlineOffset
 
 	}
 
@@ -1104,7 +1093,7 @@ THREE.EXRLoader.prototype._parser = function ( buffer ) {
 		for ( var y = 0; y < height; y ++ ) {
 
 			var y_scanline = parseUint32( bufferDataView, offset );
-			var dataSize = parseUint32( bufferDataView, offset );
+			parseUint32( bufferDataView, offset ); // dataSize
 
 			for ( var channelID = 0; channelID < EXRHeader.channels.length; channelID ++ ) {
 
@@ -1135,8 +1124,8 @@ THREE.EXRLoader.prototype._parser = function ( buffer ) {
 
 		for ( var scanlineBlockIdx = 0; scanlineBlockIdx < height / scanlineBlockSize; scanlineBlockIdx ++ ) {
 
-			var line_no = parseUint32( bufferDataView, offset );
-			var data_len = parseUint32( bufferDataView, offset );
+			parseUint32( bufferDataView, offset ); // line_no
+			parseUint32( bufferDataView, offset ); // data_len
 
 			var tmpBufferSize = width * scanlineBlockSize * ( EXRHeader.channels.length * BYTES_PER_HALF );
 			var tmpBuffer = new Uint16Array( tmpBufferSize );

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

@@ -68,6 +68,12 @@ THREE.GLTFLoader = ( function () {
 			loader.setPath( this.path );
 			loader.setResponseType( 'arraybuffer' );
 
+			if ( scope.crossOrigin === 'use-credentials' ) {
+
+				loader.setWithCredentials( true );
+
+			}
+
 			loader.load( url, function ( data ) {
 
 				try {
@@ -1609,6 +1615,12 @@ THREE.GLTFLoader = ( function () {
 		this.fileLoader = new THREE.FileLoader( this.options.manager );
 		this.fileLoader.setResponseType( 'arraybuffer' );
 
+		if ( this.options.crossOrigin === 'use-credentials' ) {
+
+			this.fileLoader.setWithCredentials( true );
+
+		}
+
 	}
 
 	GLTFParser.prototype.parse = function ( onLoad, onError ) {
@@ -2613,7 +2625,13 @@ THREE.GLTFLoader = ( function () {
 							? new THREE.SkinnedMesh( geometry, material )
 							: new THREE.Mesh( geometry, material );
 
-						if ( mesh.isSkinnedMesh === true ) mesh.normalizeSkinWeights(); // #15319
+						if ( mesh.isSkinnedMesh === true && !mesh.geometry.attributes.skinWeight.normalized ) {
+
+							// we normalize floating point skin weight array to fix malformed assets (see #15319)
+							// it's important to skip this for non-float32 data since normalizeSkinWeights assumes non-normalized inputs
+							mesh.normalizeSkinWeights();
+
+						}
 
 						if ( primitive.mode === WEBGL_CONSTANTS.TRIANGLE_STRIP ) {
 
@@ -2863,12 +2881,52 @@ THREE.GLTFLoader = ( function () {
 
 				}
 
+				var outputArray = outputAccessor.array;
+
+				if ( outputAccessor.normalized ) {
+
+					var scale;
+
+					if ( outputArray.constructor === Int8Array ) {
+
+						scale = 1 / 127;
+
+					} else if ( outputArray.constructor === Uint8Array ) {
+
+						scale = 1 / 255;
+
+					} else if ( outputArray.constructor == Int16Array ) {
+
+						scale = 1 / 32767;
+
+					} else if ( outputArray.constructor === Uint16Array ) {
+
+						scale = 1 / 65535;
+
+					} else {
+
+						throw new Error( 'THREE.GLTFLoader: Unsupported output accessor component type.' );
+
+					}
+
+					var scaled = new Float32Array( outputArray.length );
+
+					for ( var j = 0, jl = outputArray.length; j < jl; j ++ ) {
+
+						scaled[j] = outputArray[j] * scale;
+
+					}
+
+					outputArray = scaled;
+
+				}
+
 				for ( var j = 0, jl = targetNames.length; j < jl; j ++ ) {
 
 					var track = new TypedKeyframeTrack(
 						targetNames[ j ] + '.' + PATH_PROPERTIES[ target.path ],
 						inputAccessor.array,
-						outputAccessor.array,
+						outputArray,
 						interpolation
 					);
 

+ 321 - 303
examples/js/loaders/LDrawLoader.js

@@ -593,312 +593,17 @@ THREE.LDrawLoader = ( function () {
 			fileLoader.setPath( this.path );
 			fileLoader.load( url, function ( text ) {
 
-				processObject( text, onLoad );
+				scope.processObject( text, onLoad, null, url );
 
 			}, onProgress, onError );
 
-			function processObject( text, onProcessed, subobject ) {
-
-				var parseScope = scope.newParseScopeLevel();
-				parseScope.url = url;
-
-				var parentParseScope = scope.getParentParseScope();
-
-				// Set current matrix
-				if ( subobject ) {
-
-					parseScope.currentMatrix.multiplyMatrices( parentParseScope.currentMatrix, subobject.matrix );
-					parseScope.matrix.copy( subobject.matrix );
-					parseScope.inverted = subobject.inverted;
-
-				}
-
-				// Add to cache
-				var currentFileName = parentParseScope.currentFileName;
-				if ( currentFileName !== null ) {
-
-					currentFileName = parentParseScope.currentFileName.toLowerCase();
-
-				}
-
-				if ( scope.subobjectCache[ currentFileName ] === undefined ) {
-
-					scope.subobjectCache[ currentFileName ] = text;
-
-				}
-
-
-				// Parse the object (returns a THREE.Group)
-				scope.parse( text );
-				var finishedCount = 0;
-				onSubobjectFinish();
-
-				function onSubobjectFinish() {
-
-					finishedCount ++;
-
-					if ( finishedCount === parseScope.subobjects.length + 1 ) {
-
-						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 );
-
-						} );
-						parseScope.subobjectIndex ++;
-
-					}
-
-				}
-
-				function finalizeObject() {
-
-					if ( scope.smoothNormals && parseScope.type === 'Part' ) {
-
-						smoothNormals( parseScope.triangles, parseScope.lineSegments );
-
-					}
-
-					var isRoot = ! parentParseScope.isFromParse;
-					if ( scope.separateObjects && ! isPrimitiveType( parseScope.type ) || isRoot ) {
-
-
-						const objGroup = parseScope.groupObject;
-						if ( parseScope.triangles.length > 0 ) {
-
-							objGroup.add( createObject( parseScope.triangles, 3 ) );
-
-						}
-
-						if ( parseScope.lineSegments.length > 0 ) {
-
-							objGroup.add( createObject( parseScope.lineSegments, 2 ) );
-
-						}
-
-						if ( parseScope.conditionalSegments.length > 0 ) {
-
-							objGroup.add( createObject( parseScope.conditionalSegments, 2, true ) );
-
-						}
-
-						if ( parentParseScope.groupObject ) {
-
-							objGroup.name = parseScope.fileName;
-							objGroup.userData.category = parseScope.category;
-							objGroup.userData.keywords = parseScope.keywords;
-							parseScope.matrix.decompose( objGroup.position, objGroup.quaternion, objGroup.scale );
-
-							parentParseScope.groupObject.add( objGroup );
-
-						}
-
-					} else {
-
-						var separateObjects = scope.separateObjects;
-						var parentLineSegments = parentParseScope.lineSegments;
-						var parentConditionalSegments = parentParseScope.conditionalSegments;
-						var parentTriangles = parentParseScope.triangles;
-
-						var lineSegments = parseScope.lineSegments;
-						var conditionalSegments = parseScope.conditionalSegments;
-						var triangles = parseScope.triangles;
-
-						for ( var i = 0, l = lineSegments.length; i < l; i ++ ) {
-
-							var ls = lineSegments[ i ];
-							if ( separateObjects ) {
-
-								ls.v0.applyMatrix4( parseScope.matrix );
-								ls.v1.applyMatrix4( parseScope.matrix );
-
-							}
-							parentLineSegments.push( ls );
-
-						}
-
-						for ( var i = 0, l = conditionalSegments.length; i < l; i ++ ) {
-
-							var os = conditionalSegments[ i ];
-							if ( separateObjects ) {
-
-								os.v0.applyMatrix4( parseScope.matrix );
-								os.v1.applyMatrix4( parseScope.matrix );
-								os.c0.applyMatrix4( parseScope.matrix );
-								os.c1.applyMatrix4( parseScope.matrix );
-
-							}
-							parentConditionalSegments.push( os );
-
-						}
-
-						for ( var i = 0, l = triangles.length; i < l; i ++ ) {
-
-							var tri = triangles[ i ];
-							if ( separateObjects ) {
-
-								tri.v0 = tri.v0.clone().applyMatrix4( parseScope.matrix );
-								tri.v1 = tri.v1.clone().applyMatrix4( parseScope.matrix );
-								tri.v2 = tri.v2.clone().applyMatrix4( parseScope.matrix );
-
-								tempVec0.subVectors( tri.v1, tri.v0 );
-								tempVec1.subVectors( tri.v2, tri.v1 );
-								tri.faceNormal.crossVectors( tempVec0, tempVec1 ).normalize();
-
-							}
-							parentTriangles.push( tri );
-
-						}
-
-					}
-
-					scope.removeScopeLevel();
-
-					if ( onProcessed ) {
-
-						onProcessed( parseScope.groupObject );
-
-					}
-
-				}
-
-				function loadSubobject( subobject ) {
-
-					parseScope.mainColourCode = subobject.material.userData.code;
-					parseScope.mainEdgeColourCode = subobject.material.userData.edgeMaterial.userData.code;
-					parseScope.currentFileName = subobject.originalFileName;
-
-
-					// If subobject was cached previously, use the cached one
-					var cached = scope.subobjectCache[ subobject.originalFileName.toLowerCase() ];
-					if ( cached ) {
-
-						processObject( cached, function ( subobjectGroup ) {
-
-							onSubobjectLoaded( subobjectGroup, subobject );
-							onSubobjectFinish();
-
-						}, subobject );
-
-						return;
-
-					}
-
-					// Adjust file name to locate the subobject file path in standard locations (always under directory scope.path)
-					// Update also subobject.locationState for the next try if this load fails.
-					var subobjectURL = subobject.fileName;
-					var newLocationState = LDrawLoader.FILE_LOCATION_NOT_FOUND;
-
-					switch ( subobject.locationState ) {
-
-						case LDrawLoader.FILE_LOCATION_AS_IS:
-							newLocationState = subobject.locationState + 1;
-							break;
-
-						case LDrawLoader.FILE_LOCATION_TRY_PARTS:
-							subobjectURL = 'parts/' + subobjectURL;
-							newLocationState = subobject.locationState + 1;
-							break;
-
-						case LDrawLoader.FILE_LOCATION_TRY_P:
-							subobjectURL = 'p/' + subobjectURL;
-							newLocationState = subobject.locationState + 1;
-							break;
-
-						case LDrawLoader.FILE_LOCATION_TRY_MODELS:
-							subobjectURL = 'models/' + subobjectURL;
-							newLocationState = subobject.locationState + 1;
-							break;
-
-						case LDrawLoader.FILE_LOCATION_TRY_RELATIVE:
-							subobjectURL = url.substring( 0, url.lastIndexOf( "/" ) + 1 ) + subobjectURL;
-							newLocationState = subobject.locationState + 1;
-							break;
-
-						case LDrawLoader.FILE_LOCATION_TRY_ABSOLUTE:
-
-							if ( subobject.triedLowerCase ) {
-
-								// Try absolute path
-								newLocationState = LDrawLoader.FILE_LOCATION_NOT_FOUND;
-
-							} else {
-
-								// Next attempt is lower case
-								subobject.fileName = subobject.fileName.toLowerCase();
-								subobjectURL = subobject.fileName;
-								subobject.triedLowerCase = true;
-								newLocationState = LDrawLoader.FILE_LOCATION_AS_IS;
-
-							}
-							break;
-
-						case LDrawLoader.FILE_LOCATION_NOT_FOUND:
-
-							// All location possibilities have been tried, give up loading this object
-							console.warn( 'LDrawLoader: Subobject "' + subobject.originalFileName + '" could not be found.' );
-
-							return;
-
-					}
-
-					subobject.locationState = newLocationState;
-					subobject.url = subobjectURL;
-
-					// 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 ) {
-
-						processObject( text, function ( subobjectGroup ) {
-
-							onSubobjectLoaded( subobjectGroup, subobject );
-							onSubobjectFinish();
-
-						}, subobject );
-
-					}, undefined, function ( err ) {
-
-						onSubobjectError( err, subobject );
-
-					}, subobject );
-
-				}
-
-				function onSubobjectLoaded( subobjectGroup, subobject ) {
-
-					if ( subobjectGroup === null ) {
-
-						// Try to reload
-						loadSubobject( subobject );
-						return;
-
-					}
-
-					scope.fileMap[ subobject.originalFileName ] = subobject.url;
-
-				}
-
-				function onSubobjectError( err, subobject ) {
+		},
 
-					// Retry download from a different default possible location
-					loadSubobject( subobject );
+		parse: function ( text, path, onLoad ) {
 
-				}
+			// Async parse.  This function calls onParse with the parsed THREE.Object3D as parameter
 
-			}
+			this.processObject( text, onLoad, null, path );
 
 		},
 
@@ -1273,6 +978,7 @@ THREE.LDrawLoader = ( function () {
 			material.transparent = isTransparent;
 			material.premultipliedAlpha = true;
 			material.opacity = alpha;
+			material.depthWrite = ! isTransparent;
 
 			material.polygonOffset = true;
 			material.polygonOffsetFactor = 1;
@@ -1288,7 +994,12 @@ THREE.LDrawLoader = ( function () {
 			if ( ! edgeMaterial ) {
 
 				// This is the material used for edges
-				edgeMaterial = new THREE.LineBasicMaterial( { color: edgeColour } );
+				edgeMaterial = new THREE.LineBasicMaterial( {
+					color: edgeColour,
+					transparent: isTransparent,
+					opacity: alpha,
+					depthWrite: ! isTransparent
+				} );
 				edgeMaterial.userData.code = code;
 				edgeMaterial.name = name + " - Edge";
 				edgeMaterial.userData.canHaveEnvMap = false;
@@ -1304,7 +1015,9 @@ THREE.LDrawLoader = ( function () {
 						opacity: {
 							value: alpha
 						}
-					}
+					},
+					transparent: isTransparent,
+					depthWrite: ! isTransparent
 				} );
 				edgeMaterial.userData.conditionalEdgeMaterial.userData.canHaveEnvMap = false;
 
@@ -1321,7 +1034,7 @@ THREE.LDrawLoader = ( function () {
 
 		//
 
-		parse: function ( text ) {
+		objectParse: function ( text ) {
 
 			//console.time( 'LDrawLoader' );
 
@@ -1880,6 +1593,311 @@ THREE.LDrawLoader = ( function () {
 			currentParseScope.numSubobjects = subobjects.length;
 			currentParseScope.subobjectIndex = 0;
 
+		},
+
+		processObject: function ( text, onProcessed, subobject, url ) {
+
+			var scope = this;
+
+			var parseScope = scope.newParseScopeLevel();
+			parseScope.url = url;
+
+			var parentParseScope = scope.getParentParseScope();
+
+			// Set current matrix
+			if ( subobject ) {
+
+				parseScope.currentMatrix.multiplyMatrices( parentParseScope.currentMatrix, subobject.matrix );
+				parseScope.matrix.copy( subobject.matrix );
+				parseScope.inverted = subobject.inverted;
+
+			}
+
+			// Add to cache
+			var currentFileName = parentParseScope.currentFileName;
+			if ( currentFileName !== null ) {
+
+				currentFileName = parentParseScope.currentFileName.toLowerCase();
+
+			}
+
+			if ( scope.subobjectCache[ currentFileName ] === undefined ) {
+
+				scope.subobjectCache[ currentFileName ] = text;
+
+			}
+
+
+			// Parse the object (returns a THREE.Group)
+			scope.objectParse( text );
+			var finishedCount = 0;
+			onSubobjectFinish();
+
+			function onSubobjectFinish() {
+
+				finishedCount ++;
+
+				if ( finishedCount === parseScope.subobjects.length + 1 ) {
+
+					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 );
+
+					} );
+					parseScope.subobjectIndex ++;
+
+				}
+
+			}
+
+			function finalizeObject() {
+
+				if ( scope.smoothNormals && parseScope.type === 'Part' ) {
+
+					smoothNormals( parseScope.triangles, parseScope.lineSegments );
+
+				}
+
+				var isRoot = ! parentParseScope.isFromParse;
+				if ( scope.separateObjects && ! isPrimitiveType( parseScope.type ) || isRoot ) {
+
+
+					const objGroup = parseScope.groupObject;
+					if ( parseScope.triangles.length > 0 ) {
+
+						objGroup.add( createObject( parseScope.triangles, 3 ) );
+
+					}
+
+					if ( parseScope.lineSegments.length > 0 ) {
+
+						objGroup.add( createObject( parseScope.lineSegments, 2 ) );
+
+					}
+
+					if ( parseScope.conditionalSegments.length > 0 ) {
+
+						objGroup.add( createObject( parseScope.conditionalSegments, 2, true ) );
+
+					}
+
+					if ( parentParseScope.groupObject ) {
+
+						objGroup.name = parseScope.fileName;
+						objGroup.userData.category = parseScope.category;
+						objGroup.userData.keywords = parseScope.keywords;
+						parseScope.matrix.decompose( objGroup.position, objGroup.quaternion, objGroup.scale );
+
+						parentParseScope.groupObject.add( objGroup );
+
+					}
+
+				} else {
+
+					var separateObjects = scope.separateObjects;
+					var parentLineSegments = parentParseScope.lineSegments;
+					var parentConditionalSegments = parentParseScope.conditionalSegments;
+					var parentTriangles = parentParseScope.triangles;
+
+					var lineSegments = parseScope.lineSegments;
+					var conditionalSegments = parseScope.conditionalSegments;
+					var triangles = parseScope.triangles;
+
+					for ( var i = 0, l = lineSegments.length; i < l; i ++ ) {
+
+						var ls = lineSegments[ i ];
+						if ( separateObjects ) {
+
+							ls.v0.applyMatrix4( parseScope.matrix );
+							ls.v1.applyMatrix4( parseScope.matrix );
+
+						}
+						parentLineSegments.push( ls );
+
+					}
+
+					for ( var i = 0, l = conditionalSegments.length; i < l; i ++ ) {
+
+						var os = conditionalSegments[ i ];
+						if ( separateObjects ) {
+
+							os.v0.applyMatrix4( parseScope.matrix );
+							os.v1.applyMatrix4( parseScope.matrix );
+							os.c0.applyMatrix4( parseScope.matrix );
+							os.c1.applyMatrix4( parseScope.matrix );
+
+						}
+						parentConditionalSegments.push( os );
+
+					}
+
+					for ( var i = 0, l = triangles.length; i < l; i ++ ) {
+
+						var tri = triangles[ i ];
+						if ( separateObjects ) {
+
+							tri.v0 = tri.v0.clone().applyMatrix4( parseScope.matrix );
+							tri.v1 = tri.v1.clone().applyMatrix4( parseScope.matrix );
+							tri.v2 = tri.v2.clone().applyMatrix4( parseScope.matrix );
+
+							tempVec0.subVectors( tri.v1, tri.v0 );
+							tempVec1.subVectors( tri.v2, tri.v1 );
+							tri.faceNormal.crossVectors( tempVec0, tempVec1 ).normalize();
+
+						}
+						parentTriangles.push( tri );
+
+					}
+
+				}
+
+				scope.removeScopeLevel();
+
+				if ( onProcessed ) {
+
+					onProcessed( parseScope.groupObject );
+
+				}
+
+			}
+
+			function loadSubobject( subobject ) {
+
+				parseScope.mainColourCode = subobject.material.userData.code;
+				parseScope.mainEdgeColourCode = subobject.material.userData.edgeMaterial.userData.code;
+				parseScope.currentFileName = subobject.originalFileName;
+
+
+				// If subobject was cached previously, use the cached one
+				var cached = scope.subobjectCache[ subobject.originalFileName.toLowerCase() ];
+				if ( cached ) {
+
+					scope.processObject( cached, function ( subobjectGroup ) {
+
+						onSubobjectLoaded( subobjectGroup, subobject );
+						onSubobjectFinish();
+
+					}, subobject, url );
+
+					return;
+
+				}
+
+				// Adjust file name to locate the subobject file path in standard locations (always under directory scope.path)
+				// Update also subobject.locationState for the next try if this load fails.
+				var subobjectURL = subobject.fileName;
+				var newLocationState = LDrawLoader.FILE_LOCATION_NOT_FOUND;
+
+				switch ( subobject.locationState ) {
+
+					case LDrawLoader.FILE_LOCATION_AS_IS:
+						newLocationState = subobject.locationState + 1;
+						break;
+
+					case LDrawLoader.FILE_LOCATION_TRY_PARTS:
+						subobjectURL = 'parts/' + subobjectURL;
+						newLocationState = subobject.locationState + 1;
+						break;
+
+					case LDrawLoader.FILE_LOCATION_TRY_P:
+						subobjectURL = 'p/' + subobjectURL;
+						newLocationState = subobject.locationState + 1;
+						break;
+
+					case LDrawLoader.FILE_LOCATION_TRY_MODELS:
+						subobjectURL = 'models/' + subobjectURL;
+						newLocationState = subobject.locationState + 1;
+						break;
+
+					case LDrawLoader.FILE_LOCATION_TRY_RELATIVE:
+						subobjectURL = url.substring( 0, url.lastIndexOf( "/" ) + 1 ) + subobjectURL;
+						newLocationState = subobject.locationState + 1;
+						break;
+
+					case LDrawLoader.FILE_LOCATION_TRY_ABSOLUTE:
+
+						if ( subobject.triedLowerCase ) {
+
+							// Try absolute path
+							newLocationState = LDrawLoader.FILE_LOCATION_NOT_FOUND;
+
+						} else {
+
+							// Next attempt is lower case
+							subobject.fileName = subobject.fileName.toLowerCase();
+							subobjectURL = subobject.fileName;
+							subobject.triedLowerCase = true;
+							newLocationState = LDrawLoader.FILE_LOCATION_AS_IS;
+
+						}
+						break;
+
+					case LDrawLoader.FILE_LOCATION_NOT_FOUND:
+
+						// All location possibilities have been tried, give up loading this object
+						console.warn( 'LDrawLoader: Subobject "' + subobject.originalFileName + '" could not be found.' );
+
+						return;
+
+				}
+
+				subobject.locationState = newLocationState;
+				subobject.url = subobjectURL;
+
+				// 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 ) {
+
+					scope.processObject( text, function ( subobjectGroup ) {
+
+						onSubobjectLoaded( subobjectGroup, subobject );
+						onSubobjectFinish();
+
+					}, subobject, url );
+
+				}, undefined, function ( err ) {
+
+					onSubobjectError( err, subobject );
+
+				}, subobject );
+
+			}
+
+			function onSubobjectLoaded( subobjectGroup, subobject ) {
+
+				if ( subobjectGroup === null ) {
+
+					// Try to reload
+					loadSubobject( subobject );
+					return;
+
+				}
+
+				scope.fileMap[ subobject.originalFileName ] = subobject.url;
+
+			}
+
+			function onSubobjectError( err, subobject ) {
+
+				// Retry download from a different default possible location
+				loadSubobject( subobject );
+
+			}
+
 		}
 
 	};

+ 2237 - 1634
examples/js/loaders/LWOLoader.js

@@ -1,7 +1,10 @@
 /**
+ * @version 1.1.1
+ *
  * @author Lewy Blue https://github.com/looeee
+ * @author Guilherme Avila https://github/sciecode
  *
- * Load files in LWO3 and LWO2 format
+ * @desc Load files in LWO3 and LWO2 format on Three.js
  *
  * LWO3 format specification:
  * 	http://static.lightwave3d.com/sdk/2018/html/filefmts/lwo3.html
@@ -9,2444 +12,3044 @@
  * LWO2 format specification:
  * 	http://static.lightwave3d.com/sdk/2018/html/filefmts/lwo2.html
  *
- */
+ * Development and test repository:
+ *	https://github.com/threejs/lwoloader
+ *
+ **/
 
-THREE.LWOLoader = ( function () {
+function LWO2Parser( IFFParser ) {
 
-	var lwoTree;
+	this.IFF = IFFParser;
 
-	function LWOLoader( manager ) {
+}
 
-		this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
+LWO2Parser.prototype = {
 
-	}
+	constructor: LWO2Parser,
 
-	LWOLoader.prototype = {
+	parseBlock: function () {
 
-		constructor: LWOLoader,
+		this.IFF.debugger.offset = this.IFF.reader.offset;
+		this.IFF.debugger.closeForms();
 
-		crossOrigin: 'anonymous',
+		var blockID = this.IFF.reader.getIDTag();
+		var length = this.IFF.reader.getUint32(); // size of data in bytes
+		if ( length > this.IFF.reader.dv.byteLength - this.IFF.reader.offset ) {
 
-		load: function ( url, onLoad, onProgress, onError ) {
+			this.IFF.reader.offset -= 4;
+			length = this.IFF.reader.getUint16();
 
-			var self = this;
+		}
 
-			var path = ( self.path === undefined ) ? THREE.LoaderUtils.extractUrlBase( url ) : self.path;
+		this.IFF.debugger.dataOffset = this.IFF.reader.offset;
+		this.IFF.debugger.length = length;
+
+		// Data types may be found in either LWO2 OR LWO3 spec
+		switch ( blockID ) {
+
+			case 'FORM': // form blocks may consist of sub -chunks or sub-forms
+				this.IFF.parseForm( length );
+				break;
+
+			// SKIPPED CHUNKS
+			// if break; is called directly, the position in the lwoTree is not created
+			// any sub chunks and forms are added to the parent form instead
+			// MISC skipped
+			case 'ICON': // Thumbnail Icon Image
+			case 'VMPA': // Vertex Map Parameter
+			case 'BBOX': // bounding box
+			// case 'VMMD':
+			// case 'VTYP':
+
+			// normal maps can be specified, normally on models imported from other applications. Currently ignored
+			case 'NORM':
+
+			// ENVL FORM skipped
+			case 'PRE ':
+			case 'POST':
+			case 'KEY ':
+			case 'SPAN':
+
+			// CLIP FORM skipped
+			case 'TIME':
+			case 'CLRS':
+			case 'CLRA':
+			case 'FILT':
+			case 'DITH':
+			case 'CONT':
+			case 'BRIT':
+			case 'SATR':
+			case 'HUE ':
+			case 'GAMM':
+			case 'NEGA':
+			case 'IFLT':
+			case 'PFLT':
+
+			// Image Map Layer skipped
+			case 'PROJ':
+			case 'AXIS':
+			case 'AAST':
+			case 'PIXB':
+			case 'AUVO':
+			case 'STCK':
+
+			// Procedural Textures skipped
+			case 'PROC':
+			case 'VALU':
+			case 'FUNC':
+
+			// Gradient Textures skipped
+			case 'PNAM':
+			case 'INAM':
+			case 'GRST':
+			case 'GREN':
+			case 'GRPT':
+			case 'FKEY':
+			case 'IKEY':
+
+			// Texture Mapping Form skipped
+			case 'CSYS':
+
+			// Surface CHUNKs skipped
+			case 'OPAQ': // top level 'opacity' checkbox
+			case 'CMAP': // clip map
+
+			// Surface node CHUNKS skipped
+			// These mainly specify the node editor setup in LW
+			case 'NLOC':
+			case 'NZOM':
+			case 'NVER':
+			case 'NSRV':
+			case 'NVSK': // unknown
+			case 'NCRD':
+			case 'WRPW': // image wrap w ( for cylindrical and spherical projections)
+			case 'WRPH': // image wrap h
+			case 'NMOD':
+			case 'NPRW':
+			case 'NPLA':
+			case 'NODS':
+			case 'VERS':
+			case 'ENUM':
+			case 'TAG ':
+			case 'OPAC':
+
+			// Car Material CHUNKS
+			case 'CGMD':
+			case 'CGTY':
+			case 'CGST':
+			case 'CGEN':
+			case 'CGTS':
+			case 'CGTE':
+			case 'OSMP':
+			case 'OMDE':
+			case 'OUTR':
+			case 'FLAG':
+
+			case 'TRNL':
+			case 'GLOW':
+			case 'GVAL': // glow intensity
+			case 'SHRP':
+			case 'RFOP':
+			case 'RSAN':
+			case 'TROP':
+			case 'RBLR':
+			case 'TBLR':
+			case 'CLRH':
+			case 'CLRF':
+			case 'ADTR':
+			case 'LINE':
+			case 'ALPH':
+			case 'VCOL':
+			case 'ENAB':
+				this.IFF.debugger.skipped = true;
+				this.IFF.reader.skip( length );
+				break;
+
+			case 'SURF':
+				this.IFF.parseSurfaceLwo2( length );
+				break;
+
+			case 'CLIP':
+				this.IFF.parseClipLwo2( length );
+				break;
+
+			// Texture node chunks (not in spec)
+			case 'IPIX': // usePixelBlending
+			case 'IMIP': // useMipMaps
+			case 'IMOD': // imageBlendingMode
+			case 'AMOD': // unknown
+			case 'IINV': // imageInvertAlpha
+			case 'INCR': // imageInvertColor
+			case 'IAXS': // imageAxis ( for non-UV maps)
+			case 'IFOT': // imageFallofType
+			case 'ITIM': // timing for animated textures
+			case 'IWRL':
+			case 'IUTI':
+			case 'IINX':
+			case 'IINY':
+			case 'IINZ':
+			case 'IREF': // possibly a VX for reused texture nodes
+				if ( length === 4 ) this.IFF.currentNode[ blockID ] = this.IFF.reader.getInt32();
+				else this.IFF.reader.skip( length );
+				break;
+
+			case 'OTAG':
+				this.IFF.parseObjectTag();
+				break;
+
+			case 'LAYR':
+				this.IFF.parseLayer( length );
+				break;
+
+			case 'PNTS':
+				this.IFF.parsePoints( length );
+				break;
+
+			case 'VMAP':
+				this.IFF.parseVertexMapping( length );
+				break;
+
+			case 'AUVU':
+			case 'AUVN':
+				this.IFF.reader.skip( length - 1 );
+				this.IFF.reader.getVariableLengthIndex(); // VX
+				break;
+
+			case 'POLS':
+				this.IFF.parsePolygonList( length );
+				break;
+
+			case 'TAGS':
+				this.IFF.parseTagStrings( length );
+				break;
+
+			case 'PTAG':
+				this.IFF.parsePolygonTagMapping( length );
+				break;
+
+			case 'VMAD':
+				this.IFF.parseVertexMapping( length, true );
+				break;
+
+			// Misc CHUNKS
+			case 'DESC': // Description Line
+				this.IFF.currentForm.description = this.IFF.reader.getString();
+				break;
+
+			case 'TEXT':
+			case 'CMNT':
+			case 'NCOM':
+				this.IFF.currentForm.comment = this.IFF.reader.getString();
+				break;
+
+			// Envelope Form
+			case 'NAME':
+				this.IFF.currentForm.channelName = this.IFF.reader.getString();
+				break;
+
+			// Image Map Layer
+			case 'WRAP':
+				this.IFF.currentForm.wrap = { w: this.IFF.reader.getUint16(), h: this.IFF.reader.getUint16() };
+				break;
+
+			case 'IMAG':
+				var index = this.IFF.reader.getVariableLengthIndex();
+				this.IFF.currentForm.imageIndex = index;
+				break;
+
+			// Texture Mapping Form
+			case 'OREF':
+				this.IFF.currentForm.referenceObject = this.IFF.reader.getString();
+				break;
+
+			case 'ROID':
+				this.IFF.currentForm.referenceObjectID = this.IFF.reader.getUint32();
+				break;
+
+			// Surface Blocks
+			case 'SSHN':
+				this.IFF.currentSurface.surfaceShaderName = this.IFF.reader.getString();
+				break;
+
+			case 'AOVN':
+				this.IFF.currentSurface.surfaceCustomAOVName = this.IFF.reader.getString();
+				break;
+
+			// Nodal Blocks
+			case 'NSTA':
+				this.IFF.currentForm.disabled = this.IFF.reader.getUint16();
+				break;
+
+			case 'NRNM':
+				this.IFF.currentForm.realName = this.IFF.reader.getString();
+				break;
+
+			case 'NNME':
+				this.IFF.currentForm.refName = this.IFF.reader.getString();
+				this.IFF.currentSurface.nodes[ this.IFF.currentForm.refName ] = this.IFF.currentForm;
+				break;
+
+			// Nodal Blocks : connections
+			case 'INME':
+				if ( ! this.IFF.currentForm.nodeName ) this.IFF.currentForm.nodeName = [];
+				this.IFF.currentForm.nodeName.push( this.IFF.reader.getString() );
+				break;
+
+			case 'IINN':
+				if ( ! this.IFF.currentForm.inputNodeName ) this.IFF.currentForm.inputNodeName = [];
+				this.IFF.currentForm.inputNodeName.push( this.IFF.reader.getString() );
+				break;
+
+			case 'IINM':
+				if ( ! this.IFF.currentForm.inputName ) this.IFF.currentForm.inputName = [];
+				this.IFF.currentForm.inputName.push( this.IFF.reader.getString() );
+				break;
+
+			case 'IONM':
+				if ( ! this.IFF.currentForm.inputOutputName ) this.IFF.currentForm.inputOutputName = [];
+				this.IFF.currentForm.inputOutputName.push( this.IFF.reader.getString() );
+				break;
+
+			case 'FNAM':
+				this.IFF.currentForm.fileName = this.IFF.reader.getString();
+				break;
+
+			case 'CHAN': // NOTE: ENVL Forms may also have CHAN chunk, however ENVL is currently ignored
+				if ( length === 4 ) this.IFF.currentForm.textureChannel = this.IFF.reader.getIDTag();
+				else this.IFF.reader.skip( length );
+				break;
+
+			// LWO2 Spec chunks: these are needed since the SURF FORMs are often in LWO2 format
+			case 'SMAN':
+				var maxSmoothingAngle = this.IFF.reader.getFloat32();
+				this.IFF.currentSurface.attributes.smooth = ( maxSmoothingAngle < 0 ) ? false : true;
+				break;
+
+			// LWO2: Basic Surface Parameters
+			case 'COLR':
+				this.IFF.currentSurface.attributes.Color = { value: this.IFF.reader.getFloat32Array( 3 ) };
+				this.IFF.reader.skip( 2 ); // VX: envelope
+				break;
+
+			case 'LUMI':
+				this.IFF.currentSurface.attributes.Luminosity = { value: this.IFF.reader.getFloat32() };
+				this.IFF.reader.skip( 2 );
+				break;
+
+			case 'SPEC':
+				this.IFF.currentSurface.attributes.Specular = { value: this.IFF.reader.getFloat32() };
+				this.IFF.reader.skip( 2 );
+				break;
+
+			case 'DIFF':
+				this.IFF.currentSurface.attributes.Diffuse = { value: this.IFF.reader.getFloat32() };
+				this.IFF.reader.skip( 2 );
+				break;
+
+			case 'REFL':
+				this.IFF.currentSurface.attributes.Reflection = { value: this.IFF.reader.getFloat32() };
+				this.IFF.reader.skip( 2 );
+				break;
+
+			case 'GLOS':
+				this.IFF.currentSurface.attributes.Glossiness = { value: this.IFF.reader.getFloat32() };
+				this.IFF.reader.skip( 2 );
+				break;
+
+			case 'TRAN':
+				this.IFF.currentSurface.attributes.opacity = this.IFF.reader.getFloat32();
+				this.IFF.reader.skip( 2 );
+				break;
+
+			case 'BUMP':
+				this.IFF.currentSurface.attributes.bumpStrength = this.IFF.reader.getFloat32();
+				this.IFF.reader.skip( 2 );
+				break;
+
+			case 'SIDE':
+				this.IFF.currentSurface.attributes.side = this.IFF.reader.getUint16();
+				break;
+
+			case 'RIMG':
+				this.IFF.currentSurface.attributes.reflectionMap = this.IFF.reader.getVariableLengthIndex();
+				break;
+
+			case 'RIND':
+				this.IFF.currentSurface.attributes.refractiveIndex = this.IFF.reader.getFloat32();
+				this.IFF.reader.skip( 2 );
+				break;
+
+			case 'TIMG':
+				this.IFF.currentSurface.attributes.refractionMap = this.IFF.reader.getVariableLengthIndex();
+				break;
+
+			case 'IMAP':
+				this.IFF.reader.skip( 2 );
+				break;
+
+			case 'TMAP':
+				this.IFF.debugger.skipped = true;
+				this.IFF.reader.skip( length ); // needs implementing
+				break;
+
+			case 'IUVI': // uv channel name
+				this.IFF.currentNode.UVChannel = this.IFF.reader.getString( length );
+				break;
+
+			case 'IUTL': // widthWrappingMode: 0 = Reset, 1 = Repeat, 2 = Mirror, 3 = Edge
+				this.IFF.currentNode.widthWrappingMode = this.IFF.reader.getUint32();
+				break;
+			case 'IVTL': // heightWrappingMode
+				this.IFF.currentNode.heightWrappingMode = this.IFF.reader.getUint32();
+				break;
+
+			// LWO2 USE
+			case 'BLOK':
+				// skip
+				break;
+
+			default:
+				this.IFF.parseUnknownCHUNK( blockID, length );
 
-			// give the mesh a default name based on the filename
-			var modelName = url.split( path ).pop().split( '.' )[ 0 ];
+		}
 
-			var loader = new THREE.FileLoader( this.manager );
-			loader.setPath( self.path );
-			loader.setResponseType( 'arraybuffer' );
+		if ( blockID != 'FORM' ) {
 
-			loader.load( url, function ( buffer ) {
+			this.IFF.debugger.node = 1;
+			this.IFF.debugger.nodeID = blockID;
+			this.IFF.debugger.log();
 
-				// console.time( 'Total parsing: ' );
-				onLoad( self.parse( buffer, path, modelName ) );
-				// console.timeEnd( 'Total parsing: ' );
+		}
 
-			}, onProgress, onError );
+		if ( this.IFF.reader.offset >= this.IFF.currentFormEnd ) {
 
-		},
+			this.IFF.currentForm = this.IFF.parentForm;
 
-		setCrossOrigin: function ( value ) {
+		}
 
-			this.crossOrigin = value;
-			return this;
+	}
 
-		},
+};
+
+function LWO3Parser( IFFParser ) {
+
+	this.IFF = IFFParser;
+
+}
+
+LWO3Parser.prototype = {
+
+	constructor: LWO3Parser,
+
+	parseBlock: function () {
+
+		this.IFF.debugger.offset = this.IFF.reader.offset;
+		this.IFF.debugger.closeForms();
+
+		var blockID = this.IFF.reader.getIDTag();
+		var length = this.IFF.reader.getUint32(); // size of data in bytes
+
+		this.IFF.debugger.dataOffset = this.IFF.reader.offset;
+		this.IFF.debugger.length = length;
+
+		// Data types may be found in either LWO2 OR LWO3 spec
+		switch ( blockID ) {
+
+			case 'FORM': // form blocks may consist of sub -chunks or sub-forms
+				this.IFF.parseForm( length );
+				break;
+
+			// SKIPPED CHUNKS
+			// MISC skipped
+			case 'ICON': // Thumbnail Icon Image
+			case 'VMPA': // Vertex Map Parameter
+			case 'BBOX': // bounding box
+			// case 'VMMD':
+			// case 'VTYP':
+
+			// normal maps can be specified, normally on models imported from other applications. Currently ignored
+			case 'NORM':
+
+			// ENVL FORM skipped
+			case 'PRE ':
+			case 'POST':
+			case 'KEY ':
+			case 'SPAN':
+
+			// CLIP FORM skipped
+			case 'TIME':
+			case 'CLRS':
+			case 'CLRA':
+			case 'FILT':
+			case 'DITH':
+			case 'CONT':
+			case 'BRIT':
+			case 'SATR':
+			case 'HUE ':
+			case 'GAMM':
+			case 'NEGA':
+			case 'IFLT':
+			case 'PFLT':
+
+			// Image Map Layer skipped
+			case 'PROJ':
+			case 'AXIS':
+			case 'AAST':
+			case 'PIXB':
+			case 'STCK':
+
+			// Procedural Textures skipped
+			case 'VALU':
+
+			// Gradient Textures skipped
+			case 'PNAM':
+			case 'INAM':
+			case 'GRST':
+			case 'GREN':
+			case 'GRPT':
+			case 'FKEY':
+			case 'IKEY':
+
+			// Texture Mapping Form skipped
+			case 'CSYS':
+
+				// Surface CHUNKs skipped
+			case 'OPAQ': // top level 'opacity' checkbox
+			case 'CMAP': // clip map
+
+			// Surface node CHUNKS skipped
+			// These mainly specify the node editor setup in LW
+			case 'NLOC':
+			case 'NZOM':
+			case 'NVER':
+			case 'NSRV':
+			case 'NCRD':
+			case 'NMOD':
+			case 'NSEL':
+			case 'NPRW':
+			case 'NPLA':
+			case 'VERS':
+			case 'ENUM':
+			case 'TAG ':
+
+			// Car Material CHUNKS
+			case 'CGMD':
+			case 'CGTY':
+			case 'CGST':
+			case 'CGEN':
+			case 'CGTS':
+			case 'CGTE':
+			case 'OSMP':
+			case 'OMDE':
+			case 'OUTR':
+			case 'FLAG':
+
+			case 'TRNL':
+			case 'GLOS':
+			case 'SHRP':
+			case 'RFOP':
+			case 'RSAN':
+			case 'TROP':
+			case 'RBLR':
+			case 'TBLR':
+			case 'CLRH':
+			case 'CLRF':
+			case 'ADTR':
+			case 'GLOW':
+			case 'LINE':
+			case 'ALPH':
+			case 'VCOL':
+			case 'ENAB':
+				this.IFF.debugger.skipped = true;
+				this.IFF.reader.skip( length );
+				break;
+
+			// Texture node chunks (not in spec)
+			case 'IPIX': // usePixelBlending
+			case 'IMIP': // useMipMaps
+			case 'IMOD': // imageBlendingMode
+			case 'AMOD': // unknown
+			case 'IINV': // imageInvertAlpha
+			case 'INCR': // imageInvertColor
+			case 'IAXS': // imageAxis ( for non-UV maps)
+			case 'IFOT': // imageFallofType
+			case 'ITIM': // timing for animated textures
+			case 'IWRL':
+			case 'IUTI':
+			case 'IINX':
+			case 'IINY':
+			case 'IINZ':
+			case 'IREF': // possibly a VX for reused texture nodes
+				if ( length === 4 ) this.IFF.currentNode[ blockID ] = this.IFF.reader.getInt32();
+				else this.IFF.reader.skip( length );
+				break;
+
+			case 'OTAG':
+				this.IFF.parseObjectTag();
+				break;
+
+			case 'LAYR':
+				this.IFF.parseLayer( length );
+				break;
+
+			case 'PNTS':
+				this.IFF.parsePoints( length );
+				break;
+
+			case 'VMAP':
+				this.IFF.parseVertexMapping( length );
+				break;
+
+			case 'POLS':
+				this.IFF.parsePolygonList( length );
+				break;
+
+			case 'TAGS':
+				this.IFF.parseTagStrings( length );
+				break;
+
+			case 'PTAG':
+				this.IFF.parsePolygonTagMapping( length );
+				break;
+
+			case 'VMAD':
+				this.IFF.parseVertexMapping( length, true );
+				break;
+
+			// Misc CHUNKS
+			case 'DESC': // Description Line
+				this.IFF.currentForm.description = this.IFF.reader.getString();
+				break;
+
+			case 'TEXT':
+			case 'CMNT':
+			case 'NCOM':
+				this.IFF.currentForm.comment = this.IFF.reader.getString();
+				break;
+
+			// Envelope Form
+			case 'NAME':
+				this.IFF.currentForm.channelName = this.IFF.reader.getString();
+				break;
+
+			// Image Map Layer
+			case 'WRAP':
+				this.IFF.currentForm.wrap = { w: this.IFF.reader.getUint16(), h: this.IFF.reader.getUint16() };
+				break;
+
+			case 'IMAG':
+				var index = this.IFF.reader.getVariableLengthIndex();
+				this.IFF.currentForm.imageIndex = index;
+				break;
+
+			// Texture Mapping Form
+			case 'OREF':
+				this.IFF.currentForm.referenceObject = this.IFF.reader.getString();
+				break;
+
+			case 'ROID':
+				this.IFF.currentForm.referenceObjectID = this.IFF.reader.getUint32();
+				break;
+
+			// Surface Blocks
+			case 'SSHN':
+				this.IFF.currentSurface.surfaceShaderName = this.IFF.reader.getString();
+				break;
+
+			case 'AOVN':
+				this.IFF.currentSurface.surfaceCustomAOVName = this.IFF.reader.getString();
+				break;
+
+			// Nodal Blocks
+			case 'NSTA':
+				this.IFF.currentForm.disabled = this.IFF.reader.getUint16();
+				break;
+
+			case 'NRNM':
+				this.IFF.currentForm.realName = this.IFF.reader.getString();
+				break;
+
+			case 'NNME':
+				this.IFF.currentForm.refName = this.IFF.reader.getString();
+				this.IFF.currentSurface.nodes[ this.IFF.currentForm.refName ] = this.IFF.currentForm;
+				break;
+
+			// Nodal Blocks : connections
+			case 'INME':
+				if ( ! this.IFF.currentForm.nodeName ) this.IFF.currentForm.nodeName = [];
+				this.IFF.currentForm.nodeName.push( this.IFF.reader.getString() );
+				break;
+
+			case 'IINN':
+				if ( ! this.IFF.currentForm.inputNodeName ) this.IFF.currentForm.inputNodeName = [];
+				this.IFF.currentForm.inputNodeName.push( this.IFF.reader.getString() );
+				break;
+
+			case 'IINM':
+				if ( ! this.IFF.currentForm.inputName ) this.IFF.currentForm.inputName = [];
+				this.IFF.currentForm.inputName.push( this.IFF.reader.getString() );
+				break;
+
+			case 'IONM':
+				if ( ! this.IFF.currentForm.inputOutputName ) this.IFF.currentForm.inputOutputName = [];
+				this.IFF.currentForm.inputOutputName.push( this.IFF.reader.getString() );
+				break;
+
+			case 'FNAM':
+				this.IFF.currentForm.fileName = this.IFF.reader.getString();
+				break;
+
+			case 'CHAN': // NOTE: ENVL Forms may also have CHAN chunk, however ENVL is currently ignored
+				if ( length === 4 ) this.IFF.currentForm.textureChannel = this.IFF.reader.getIDTag();
+				else this.IFF.reader.skip( length );
+				break;
+
+			// LWO2 Spec chunks: these are needed since the SURF FORMs are often in LWO2 format
+			case 'SMAN':
+				var maxSmoothingAngle = this.IFF.reader.getFloat32();
+				this.IFF.currentSurface.attributes.smooth = ( maxSmoothingAngle < 0 ) ? false : true;
+				break;
+
+			// LWO2: Basic Surface Parameters
+			case 'COLR':
+				this.IFF.currentSurface.attributes.Color = { value: this.IFF.reader.getFloat32Array( 3 ) };
+				this.IFF.reader.skip( 2 ); // VX: envelope
+				break;
+
+			case 'LUMI':
+				this.IFF.currentSurface.attributes.Luminosity = { value: this.IFF.reader.getFloat32() };
+				this.IFF.reader.skip( 2 );
+				break;
+
+			case 'SPEC':
+				this.IFF.currentSurface.attributes.Specular = { value: this.IFF.reader.getFloat32() };
+				this.IFF.reader.skip( 2 );
+				break;
+
+			case 'DIFF':
+				this.IFF.currentSurface.attributes.Diffuse = { value: this.IFF.reader.getFloat32() };
+				this.IFF.reader.skip( 2 );
+				break;
+
+			case 'REFL':
+				this.IFF.currentSurface.attributes.Reflection = { value: this.IFF.reader.getFloat32() };
+				this.IFF.reader.skip( 2 );
+				break;
+
+			case 'GLOS':
+				this.IFF.currentSurface.attributes.Glossiness = { value: this.IFF.reader.getFloat32() };
+				this.IFF.reader.skip( 2 );
+				break;
+
+			case 'TRAN':
+				this.IFF.currentSurface.attributes.opacity = this.IFF.reader.getFloat32();
+				this.IFF.reader.skip( 2 );
+				break;
+
+			case 'BUMP':
+				this.IFF.currentSurface.attributes.bumpStrength = this.IFF.reader.getFloat32();
+				this.IFF.reader.skip( 2 );
+				break;
+
+			case 'SIDE':
+				this.IFF.currentSurface.attributes.side = this.IFF.reader.getUint16();
+				break;
+
+			case 'RIMG':
+				this.IFF.currentSurface.attributes.reflectionMap = this.IFF.reader.getVariableLengthIndex();
+				break;
+
+			case 'RIND':
+				this.IFF.currentSurface.attributes.refractiveIndex = this.IFF.reader.getFloat32();
+				this.IFF.reader.skip( 2 );
+				break;
+
+			case 'TIMG':
+				this.IFF.currentSurface.attributes.refractionMap = this.IFF.reader.getVariableLengthIndex();
+				break;
+
+			case 'IMAP':
+				this.IFF.currentSurface.attributes.imageMapIndex = this.IFF.reader.getUint32();
+				break;
+
+			case 'IUVI': // uv channel name
+				this.IFF.currentNode.UVChannel = this.IFF.reader.getString( length );
+				break;
+
+			case 'IUTL': // widthWrappingMode: 0 = Reset, 1 = Repeat, 2 = Mirror, 3 = Edge
+				this.IFF.currentNode.widthWrappingMode = this.IFF.reader.getUint32();
+				break;
+			case 'IVTL': // heightWrappingMode
+				this.IFF.currentNode.heightWrappingMode = this.IFF.reader.getUint32();
+				break;
+
+			default:
+				this.IFF.parseUnknownCHUNK( blockID, length );
 
-		setPath: function ( value ) {
+		}
 
-			this.path = value;
-			return this;
+		if ( blockID != 'FORM' ) {
 
-		},
+			this.IFF.debugger.node = 1;
+			this.IFF.debugger.nodeID = blockID;
+			this.IFF.debugger.log();
 
-		setResourcePath: function ( value ) {
+		}
 
-			this.resourcePath = value;
-			return this;
+		if ( this.IFF.reader.offset >= this.IFF.currentFormEnd ) {
 
-		},
+			this.IFF.currentForm = this.IFF.parentForm;
 
-		parse: function ( iffBuffer, path, modelName ) {
+		}
 
-			lwoTree = new IFFParser().parse( iffBuffer );
+	}
 
-			// console.log( 'lwoTree', lwoTree );
+};
 
-			var textureLoader = new THREE.TextureLoader( this.manager ).setPath( this.resourcePath || path ).setCrossOrigin( this.crossOrigin );
+/**
+ * === IFFParser ===
+ * - Parses data from the IFF buffer.
+ * - LWO3 files are in IFF format and can contain the following data types, referred to by shorthand codes
+ *
+ * ATOMIC DATA TYPES
+ *  ID Tag - 4x 7 bit uppercase ASCII chars: ID4
+ *  signed integer, 1, 2, or 4 byte length: I1, I2, I4
+ *  unsigned integer, 1, 2, or 4 byte length: U1, U2, U4
+ *  float, 4 byte length: F4
+ *  string, series of ASCII chars followed by null byte (If the length of the string including the null terminating byte is odd, an extra null is added so that the data that follows will begin on an even byte boundary): S0
+ *
+ * COMPOUND DATA TYPES
+ *  Variable-length Index (index into an array or collection): U2 or U4 : VX
+ *  Color (RGB): F4 + F4 + F4: COL12
+ *  Coordinate (x, y, z): F4 + F4 + F4: VEC12
+ *  Percentage F4 data type from 0->1 with 1 = 100%: FP4
+ *  Angle in radian F4: ANG4
+ *  Filename (string) S0: FNAM0
+ *  XValue F4 + index (VX) + optional envelope( ENVL ): XVAL
+ *  XValue vector VEC12 + index (VX) + optional envelope( ENVL ): XVAL3
+ *
+ *  The IFF file is arranged in chunks:
+ *  CHUNK = ID4 + length (U4) + length X bytes of data + optional 0 pad byte
+ *  optional 0 pad byte is there to ensure chunk ends on even boundary, not counted in size
+ *
+ * COMPOUND DATA TYPES
+ * - Chunks are combined in Forms (collections of chunks)
+ * - FORM = string 'FORM' (ID4) + length (U4) + type (ID4) + optional ( CHUNK | FORM )
+ * - CHUNKS and FORMS are collectively referred to as blocks
+ * - The entire file is contained in one top level FORM
+ *
+ **/
 
-			return new LWOTreeParser( textureLoader ).parse( modelName );
+function IFFParser( ) {
 
-		}
+	this.debugger = new Debugger();
+	// this.debugger.enable(); // un-comment to log IFF hierarchy.
 
-	};
+}
 
-	// Parse the lwoTree object
-	function LWOTreeParser( textureLoader ) {
+IFFParser.prototype = {
 
-		this.textureLoader = textureLoader;
+	constructor: IFFParser,
 
-	}
+	parse: function ( buffer ) {
 
-	LWOTreeParser.prototype = {
+		this.reader = new DataViewReader( buffer );
 
-		constructor: LWOTreeParser,
+		this.tree = {
+			materials: {},
+			layers: [],
+			tags: [],
+			textures: [],
+		};
 
-		parse: function ( modelName ) {
+		// start out at the top level to add any data before first layer is encountered
+		this.currentLayer = this.tree;
+		this.currentForm = this.tree;
 
-			this.materials = new MaterialParser( this.textureLoader ).parse();
-			this.defaultLayerName = modelName;
+		this.parseTopForm();
 
-			this.meshes = this.parseLayers();
+		if ( this.tree.format === undefined ) return;
 
-			return {
-				materials: this.materials,
-				meshes: this.meshes,
-			};
+		if ( this.tree.format === 'LWO2' ) {
 
-		},
+			this.parser = new LWO2Parser( this );
+			while ( ! this.reader.endOfFile() ) this.parser.parseBlock();
 
-		parseLayers() {
+		} else if ( this.tree.format === 'LWO3' ) {
 
-			// array of all meshes for building hierarchy
-			var meshes = [];
+			this.parser = new LWO3Parser( this );
+			while ( ! this.reader.endOfFile() ) this.parser.parseBlock();
 
-			// final array containing meshes with scene graph hierarchy set up
-			var finalMeshes = [];
+		}
 
-			var geometryParser = new GeometryParser();
+		this.debugger.offset = this.reader.offset;
+		this.debugger.closeForms();
 
-			var self = this;
-			lwoTree.layers.forEach( function ( layer ) {
+		return this.tree;
 
-				var geometry = geometryParser.parse( layer.geometry, layer );
+	},
 
-				var mesh = self.parseMesh( geometry, layer );
+	parseTopForm() {
 
-				meshes[ layer.number ] = mesh;
+		this.debugger.offset = this.reader.offset;
 
-				if ( layer.parent === - 1 ) finalMeshes.push( mesh );
-				else meshes[ layer.parent ].add( mesh );
+		var topForm = this.reader.getIDTag();
 
+		if ( topForm !== 'FORM' ) {
 
-			} );
+			console.warn( "LWOLoader: Top-level FORM missing." );
+			return;
 
-			this.applyPivots( finalMeshes );
+		}
 
-			return finalMeshes;
+		var length = this.reader.getUint32();
 
-		},
+		this.debugger.dataOffset = this.reader.offset;
+		this.debugger.length = length;
 
-		parseMesh( geometry, layer ) {
+		var type = this.reader.getIDTag();
 
-			var mesh;
+		if ( type === 'LWO2' ) {
 
-			var materials = this.getMaterials( geometry.userData.matNames, layer.geometry.type );
+			this.tree.format = type;
 
-			this.duplicateUVs( geometry, materials );
+		} else if ( type === 'LWO3' ) {
 
-			if ( layer.geometry.type === 'points' ) mesh = new THREE.Points( geometry, materials );
-			else if ( layer.geometry.type === 'lines' ) mesh = new THREE.LineSegments( geometry, materials );
-			else mesh = new THREE.Mesh( geometry, materials );
+			this.tree.format = type;
 
-			if ( layer.name ) mesh.name = layer.name;
-			else mesh.name = this.defaultLayerName + '_layer_' + layer.number;
+		}
 
-			mesh.userData.pivot = layer.pivot;
+		this.debugger.node = 0;
+		this.debugger.nodeID = type;
+		this.debugger.log();
+
+		return;
+
+	},
+
+
+	///
+	// FORM PARSING METHODS
+	///
+
+	// Forms are organisational and can contain any number of sub chunks and sub forms
+	// FORM ::= 'FORM'[ID4], length[U4], type[ID4], ( chunk[CHUNK] | form[FORM] ) * }
+	parseForm( length ) {
+
+		var type = this.reader.getIDTag();
+
+		switch ( type ) {
+
+			// SKIPPED FORMS
+			// if skipForm( length ) is called, the entire form and any sub forms and chunks are skipped
+
+			case 'ISEQ': // Image sequence
+			case 'ANIM': // plug in animation
+			case 'STCC': // Color-cycling Still
+			case 'VPVL':
+			case 'VPRM':
+			case 'NROT':
+			case 'WRPW': // image wrap w ( for cylindrical and spherical projections)
+			case 'WRPH': // image wrap h
+			case 'FUNC':
+			case 'FALL':
+			case 'OPAC':
+			case 'GRAD': // gradient texture
+			case 'ENVS':
+			case 'VMOP':
+			case 'VMBG':
+
+			// Car Material FORMS
+			case 'OMAX':
+			case 'STEX':
+			case 'CKBG':
+			case 'CKEY':
+			case 'VMLA':
+			case 'VMLB':
+				this.debugger.skipped = true;
+				this.skipForm( length ); // not currently supported
+				break;
+
+			// if break; is called directly, the position in the lwoTree is not created
+			// any sub chunks and forms are added to the parent form instead
+			case 'META':
+			case 'NNDS':
+			case 'NODS':
+			case 'NDTA':
+			case 'ADAT':
+			case 'AOVS':
+			case 'BLOK':
+
+			// used by texture nodes
+			case 'IBGC': // imageBackgroundColor
+			case 'IOPC': // imageOpacity
+			case 'IIMG': // hold reference to image path
+			case 'TXTR':
+				// this.setupForm( type, length );
+				this.debugger.length = 4;
+				this.debugger.skipped = true;
+				break;
+
+			case 'IFAL': // imageFallof
+			case 'ISCL': // imageScale
+			case 'IPOS': // imagePosition
+			case 'IROT': // imageRotation
+			case 'IBMP':
+			case 'IUTD':
+			case 'IVTD':
+				this.parseTextureNodeAttribute( type );
+				break;
+
+			case 'ENVL':
+				this.parseEnvelope( length );
+				break;
+
+				// CLIP FORM AND SUB FORMS
+
+			case 'CLIP':
+				if ( this.tree.format === 'LWO2' ) {
 
-			return mesh;
+					this.parseForm( length );
 
-		},
+				} else {
 
-		// TODO: may need to be reversed in z to convert LWO to three.js coordinates
-		applyPivots( meshes ) {
+					this.parseClip( length );
 
-			meshes.forEach( function ( mesh ) {
+				}
+				break;
 
-				mesh.traverse( function ( child ) {
+			case 'STIL':
+				this.parseImage();
+				break;
 
-					var pivot = child.userData.pivot;
+			case 'XREF': // clone of another STIL
+				this.reader.skip( 8 ); // unknown
+				this.currentForm.referenceTexture = {
+					index: this.reader.getUint32(),
+					refName: this.reader.getString() // internal unique ref
+				};
+				break;
 
-					child.position.x += pivot[ 0 ];
-					child.position.y += pivot[ 1 ];
-					child.position.z += pivot[ 2 ];
+				// Not in spec, used by texture nodes
 
-					if ( child.parent ) {
+			case 'IMST':
+				this.parseImageStateForm( length );
+				break;
 
-						var parentPivot = child.parent.userData.pivot;
+				// SURF FORM AND SUB FORMS
 
-						child.position.x -= parentPivot[ 0 ];
-						child.position.y -= parentPivot[ 1 ];
-						child.position.z -= parentPivot[ 2 ];
+			case 'SURF':
+				this.parseSurfaceForm( length );
+				break;
 
-					}
+			case 'VALU': // Not in spec
+				this.parseValueForm( length );
+				break;
 
-				} );
+			case 'NTAG':
+				this.parseSubNode( length );
+				break;
 
-			} );
+			case 'ATTR': // BSDF Node Attributes
+			case 'SATR': // Standard Node Attributes
+				this.setupForm( 'attributes', length );
+				break;
 
-		},
+			case 'NCON':
+				this.parseConnections( length );
+				break;
 
-		getMaterials( namesArray, type ) {
+			case 'SSHA':
+				this.parentForm = this.currentForm;
+				this.currentForm = this.currentSurface;
+				this.setupForm( 'surfaceShader', length );
+				break;
 
-			var materials = [];
+			case 'SSHD':
+				this.setupForm( 'surfaceShaderData', length );
+				break;
 
-			var self = this;
+			case 'ENTR': // Not in spec
+				this.parseEntryForm( length );
+				break;
 
-			namesArray.forEach( function ( name, i ) {
+				// Image Map Layer
 
-				materials[ i ] = self.getMaterialByName( name );
+			case 'IMAP':
+				this.parseImageMap( length );
+				break;
 
-			} );
+			case 'TAMP':
+				this.parseXVAL( 'amplitude', length );
+				break;
 
-			// convert materials to line or point mats if required
-			if ( type === 'points' || type === 'lines' ) {
+				//Texture Mapping Form
 
-				materials.forEach( function ( mat, i ) {
+			case 'TMAP':
+				this.setupForm( 'textureMap', length );
+				break;
 
-					var spec = {
-						color: mat.color,
-					};
+			case 'CNTR':
+				this.parseXVAL3( 'center', length );
+				break;
 
-					if ( type === 'points' ) {
+			case 'SIZE':
+				this.parseXVAL3( 'scale', length );
+				break;
 
-						spec.size = 0.1;
-						spec.map = mat.map;
-						spec.morphTargets = mat.morphTargets;
-						materials[ i ] = new THREE.PointsMaterial( spec );
+			case 'ROTA':
+				this.parseXVAL3( 'rotation', length );
+				break;
 
-					} else if ( type === 'lines' ) {
+			default:
+				this.parseUnknownForm( type, length );
 
-						materials[ i ] = new THREE.LineBasicMaterial( spec );
+		}
 
-					}
+		this.debugger.node = 0;
+		this.debugger.nodeID = type;
+		this.debugger.log();
 
-				} );
+	},
 
-			}
+	setupForm( type, length ) {
 
-			// if there is only one material, return that directly instead of array
-			var filtered = materials.filter( Boolean );
-			if ( filtered.length === 1 ) return filtered[ 0 ];
+		if ( ! this.currentForm ) this.currentForm = this.currentNode;
 
-			return materials;
+		this.currentFormEnd = this.reader.offset + length;
+		this.parentForm = this.currentForm;
 
-		},
+		if ( ! this.currentForm[ type ] ) {
 
-		getMaterialByName( name ) {
+			this.currentForm[ type ] = {};
+			this.currentForm = this.currentForm[ type ];
 
-			return this.materials.filter( function ( m ) {
 
-				return m.name === name;
+		} else {
 
-			} )[ 0 ];
+			// should never see this unless there's a bug in the reader
+			console.warn( 'LWOLoader: form already exists on parent: ', type, this.currentForm );
 
-		},
+			this.currentForm = this.currentForm[ type ];
 
-		// If the material has an aoMap, duplicate UVs
-		duplicateUVs( geometry, materials ) {
+		}
 
-			var duplicateUVs = false;
 
-			if ( ! Array.isArray( materials ) ) {
+	},
 
-				if ( materials.aoMap ) duplicateUVs = true;
+	skipForm( length ) {
 
-			} else {
+		this.reader.skip( length - 4 );
 
-				materials.forEach( function ( material ) {
+	},
 
-					if ( material.aoMap ) duplicateUVs = true;
+	parseUnknownForm( type, length ) {
 
-				} );
+		console.warn( 'LWOLoader: unknown FORM encountered: ' + type, length );
 
-			}
+		printBuffer( this.reader.dv.buffer, this.reader.offset, length - 4 );
+		this.reader.skip( length - 4 );
 
-			if ( ! duplicateUVs ) return;
+	},
 
-			geometry.addAttribute( 'uv2', new THREE.BufferAttribute( geometry.attributes.uv.array, 2 ) );
+	parseSurfaceForm( length ) {
 
-		},
+		this.reader.skip( 8 ); // unknown Uint32 x2
 
-	};
+		var name = this.reader.getString();
 
-	function MaterialParser( textureLoader ) {
+		var surface = {
+			attributes: {}, // LWO2 style non-node attributes will go here
+			connections: {},
+			name: name,
+			inputName: name,
+			nodes: {},
+			source: this.reader.getString(),
+		};
 
-		this.textureLoader = textureLoader;
+		this.tree.materials[ name ] = surface;
+		this.currentSurface = surface;
 
-	}
+		this.parentForm = this.tree.materials;
+		this.currentForm = surface;
+		this.currentFormEnd = this.reader.offset + length;
 
-	MaterialParser.prototype = {
+	},
 
-		constructor: MaterialParser,
+	parseSurfaceLwo2( length ) {
 
-		parse: function () {
+		var name = this.reader.getString();
 
-			var materials = [];
-			this.textures = {};
+		var surface = {
+			attributes: {}, // LWO2 style non-node attributes will go here
+			connections: {},
+			name: name,
+			nodes: {},
+			source: this.reader.getString(),
+		};
 
-			for ( var name in lwoTree.materials ) {
+		this.tree.materials[ name ] = surface;
+		this.currentSurface = surface;
 
-				if ( lwoTree.format === 'LWO3' ) {
+		this.parentForm = this.tree.materials;
+		this.currentForm = surface;
+		this.currentFormEnd = this.reader.offset + length;
 
-					materials.push( this.parseMaterial( lwoTree.materials[ name ], name, lwoTree.textures ) );
+	},
 
-				} else if ( lwoTree.format === 'LWO2' ) {
+	parseSubNode( length ) {
 
-					materials.push( this.parseMaterialLwo2( lwoTree.materials[ name ], name, lwoTree.textures ) );
+		// parse the NRNM CHUNK of the subnode FORM to get
+		// a meaningful name for the subNode
+		// some subnodes can be renamed, but Input and Surface cannot
 
-				}
+		this.reader.skip( 8 ); // NRNM + length
+		var name = this.reader.getString();
 
-			}
+		var node = {
+			name: name
+		};
+		this.currentForm = node;
+		this.currentNode = node;
 
-			return materials;
+		this.currentFormEnd = this.reader.offset + length;
 
-		},
 
-		parseMaterial( materialData, name, textures ) {
+	},
 
-			var params = {
-				name: name,
-				side: this.getSide( materialData.attributes ),
-				flatShading: this.getSmooth( materialData.attributes ),
-			};
+	// collect attributes from all nodes at the top level of a surface
+	parseConnections( length ) {
 
-			var connections = this.parseConnections( materialData.connections, materialData.nodes );
+		this.currentFormEnd = this.reader.offset + length;
+		this.parentForm = this.currentForm;
 
-			var maps = this.parseTextureNodes( connections.maps );
+		this.currentForm = this.currentSurface.connections;
 
-			this.parseAttributeImageMaps( connections.attributes, textures, maps, materialData.maps );
+	},
 
-			var attributes = this.parseAttributes( connections.attributes, maps );
+	// surface node attribute data, e.g. specular, roughness etc
+	parseEntryForm( length ) {
 
-			this.parseEnvMap( connections, maps, attributes );
+		this.reader.skip( 8 ); // NAME + length
+		var name = this.reader.getString();
+		this.currentForm = this.currentNode.attributes;
 
-			params = Object.assign( maps, params );
-			params = Object.assign( params, attributes );
+		this.setupForm( name, length );
 
-			var type = connections.attributes.Roughness ? 'Standard' : 'Phong';
+	},
 
-			return new THREE[ 'Mesh' + type + 'Material' ]( params );
+	// parse values from material - doesn't match up to other LWO3 data types
+	// sub form of entry form
+	parseValueForm() {
 
-		},
+		this.reader.skip( 8 ); // unknown + length
 
-		parseMaterialLwo2( materialData, name, textures ) {
+		var valueType = this.reader.getString();
 
-			var params = {
-				name: name,
-				side: this.getSide( materialData.attributes ),
-				flatShading: this.getSmooth( materialData.attributes ),
-			};
+		if ( valueType === 'double' ) {
 
-			var attributes = this.parseAttributes( materialData.attributes, {} );
-			params = Object.assign( params, attributes );
-			return new THREE[ 'MeshPhongMaterial' ]( params );
+			this.currentForm.value = this.reader.getUint64();
 
-		},
+		} else if ( valueType === 'int' ) {
 
-		// 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!
-		getSide( attributes ) {
+			this.currentForm.value = this.reader.getUint32();
 
-			if ( ! attributes.side ) return THREE.BackSide;
+		} else if ( valueType === 'vparam' ) {
 
-			switch ( attributes.side ) {
+			this.reader.skip( 24 );
+			this.currentForm.value = this.reader.getFloat64();
 
-				case 0:
-				case 1:
-					return THREE.BackSide;
-				case 2: return THREE.FrontSide;
-				case 3: return THREE.DoubleSide;
+		} else if ( valueType === 'vparam3' ) {
 
-			}
+			this.reader.skip( 24 );
+			this.currentForm.value = this.reader.getFloat64Array( 3 );
 
-		},
+		}
 
-		getSmooth( attributes ) {
+	},
 
-			if ( ! attributes.smooth ) return true;
-			return ! attributes.smooth;
+	// holds various data about texture node image state
+	// Data other thanmipMapLevel unknown
+	parseImageStateForm() {
 
-		},
+		this.reader.skip( 8 ); // unknown
 
-		parseConnections( connections, nodes ) {
+		this.currentForm.mipMapLevel = this.reader.getFloat32();
 
-			var materialConnections = {
-				maps: {}
-			};
+	},
 
-			var inputName = connections.inputName;
-			var inputNodeName = connections.inputNodeName;
-			var nodeName = connections.nodeName;
+	// LWO2 style image data node OR LWO3 textures defined at top level in editor (not as SURF node)
+	parseImageMap( length ) {
 
-			var self = this;
-			inputName.forEach( function ( name, index ) {
+		this.currentFormEnd = this.reader.offset + length;
+		this.parentForm = this.currentForm;
 
-				if ( name === 'Material' ) {
+		if ( ! this.currentForm.maps ) this.currentForm.maps = [];
 
-					var matNode = self.getNodeByRefName( inputNodeName[ index ], nodes );
-					materialConnections.attributes = matNode.attributes;
-					materialConnections.envMap = matNode.fileName;
-					materialConnections.name = inputNodeName[ index ];
+		var map = {};
+		this.currentForm.maps.push( map );
+		this.currentForm = map;
 
-				}
+		this.reader.skip( 10 ); // unknown, could be an issue if it contains a VX
 
-			} );
+	},
 
-			nodeName.forEach( function ( name, index ) {
+	parseTextureNodeAttribute( type ) {
 
-				if ( name === materialConnections.name ) {
+		this.reader.skip( 28 ); // FORM + length + VPRM + unknown + Uint32 x2 + float32
 
-					materialConnections.maps[ inputName[ index ] ] = self.getNodeByRefName( inputNodeName[ index ], nodes );
+		this.reader.skip( 20 ); // FORM + length + VPVL + float32 + Uint32
 
-				}
+		switch ( type ) {
 
-			} );
+			case 'ISCL':
+				this.currentNode.scale = this.reader.getFloat32Array( 3 );
+				break;
+			case 'IPOS':
+				this.currentNode.position = this.reader.getFloat32Array( 3 );
+				break;
+			case 'IROT':
+				this.currentNode.rotation = this.reader.getFloat32Array( 3 );
+				break;
+			case 'IFAL':
+				this.currentNode.falloff = this.reader.getFloat32Array( 3 );
+				break;
 
-			return materialConnections;
+			case 'IBMP':
+				this.currentNode.amplitude = this.reader.getFloat32();
+				break;
+			case 'IUTD':
+				this.currentNode.uTiles = this.reader.getFloat32();
+				break;
+			case 'IVTD':
+				this.currentNode.vTiles = this.reader.getFloat32();
+				break;
 
-		},
+		}
 
-		getNodeByRefName( refName, nodes ) {
+		this.reader.skip( 2 ); // unknown
 
-			for ( var name in nodes ) {
 
-				if ( nodes[ name ].refName === refName ) return nodes[ name ];
+	},
 
-			}
+	// ENVL forms are currently ignored
+	parseEnvelope( length ) {
 
-		},
+		this.reader.skip( length - 4 ); // skipping  entirely for now
 
-		parseTextureNodes( textureNodes ) {
+	},
 
-			var maps = {};
+	///
+	// CHUNK PARSING METHODS
+	///
 
-			for ( var name in textureNodes ) {
+	// clips can either be defined inside a surface node, or at the top
+	// level and they have a different format in each case
+	parseClip( length ) {
 
-				var node = textureNodes[ name ];
-				var path = node.fileName;
+		var tag = this.reader.getIDTag();
 
-				if ( ! path ) return;
+		// inside surface node
+		if ( tag === 'FORM' ) {
 
-				var texture = this.loadTexture( path );
+			this.reader.skip( 16 );
 
-				if ( node.widthWrappingMode !== undefined ) texture.wrapS = this.getWrappingType( node.widthWrappingMode );
-				if ( node.heightWrappingMode !== undefined ) texture.wrapT = this.getWrappingType( node.heightWrappingMode );
+			this.currentNode.fileName = this.reader.getString();
 
-				switch ( name ) {
+			return;
 
-					case 'Color':
-						maps.map = texture;
-						break;
-					case 'Roughness':
-						maps.roughnessMap = texture;
-						maps.roughness = 0.5;
-						break;
-					case 'Specular':
-						maps.specularMap = texture;
-						maps.specular = 0xffffff;
-						break;
-					case 'Luminous':
-						maps.emissiveMap = texture;
-						maps.emissive = 0x808080;
-						break;
-					case 'Metallic':
-						maps.metalnessMap = texture;
-						maps.metalness = 0.5;
-						break;
-					case 'Transparency':
-					case 'Alpha':
-						maps.alphaMap = texture;
-						maps.transparent = true;
-						break;
-					case 'Normal':
-						maps.normalMap = texture;
-						if ( node.amplitude !== undefined ) maps.normalScale = new THREE.Vector2( node.amplitude, node.amplitude );
-						break;
-					case 'Bump':
-						maps.bumpMap = texture;
-						break;
+		}
 
-				}
+		// otherwise top level
+		this.reader.setOffset( this.reader.offset - 4 );
+
+		this.currentFormEnd = this.reader.offset + length;
+		this.parentForm = this.currentForm;
+
+		this.reader.skip( 8 ); // unknown
+
+		var texture = {
+			index: this.reader.getUint32()
+		};
+		this.tree.textures.push( texture );
+		this.currentForm = texture;
+
+	},
+
+	parseClipLwo2( length ) {
+
+		var texture = {
+			index: this.reader.getUint32(),
+			fileName: ""
+		};
+
+		// 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;
 
 			}
 
-			// LWO BSDF materials can have both spec and rough, but this is not valid in three
-			if ( maps.roughnessMap && maps.specularMap ) delete maps.specularMap;
-
-			return maps;
-
-		},
-
-		// maps can also be defined on individual material attributes, parse those here
-		// This occurs on Standard (Phong) surfaces
-		parseAttributeImageMaps( attributes, textures, maps ) {
-
-			for ( var name in attributes ) {
-
-				var attribute = attributes[ name ];
-
-				if ( attribute.maps ) {
-
-					var mapData = attribute.maps[ 0 ];
-
-					var path = this.getTexturePathByIndex( mapData.imageIndex, textures );
-					if ( ! path ) return;
-
-					var texture = this.loadTexture( path );
-
-					if ( mapData.wrap !== undefined ) texture.wrapS = this.getWrappingType( mapData.wrap.w );
-					if ( mapData.wrap !== undefined ) texture.wrapT = this.getWrappingType( mapData.wrap.h );
-
-					switch ( name ) {
-
-						case 'Color':
-							maps.map = texture;
-							break;
-						case 'Diffuse':
-							maps.aoMap = texture;
-							break;
-						case 'Roughness':
-							maps.roughnessMap = texture;
-							maps.roughness = 1;
-							break;
-						case 'Specular':
-							maps.specularMap = texture;
-							maps.specular = 0xffffff;
-							break;
-						case 'Luminosity':
-							maps.emissiveMap = texture;
-							maps.emissive = 0x808080;
-							break;
-						case 'Metallic':
-							maps.metalnessMap = texture;
-							maps.metalness = 1;
-							break;
-						case 'Transparency':
-						case 'Alpha':
-							maps.alphaMap = texture;
-							maps.transparent = true;
-							break;
-						case 'Normal':
-							maps.normalMap = texture;
-							break;
-						case 'Bump':
-							maps.bumpMap = texture;
-							break;
-
-					}
+			if ( n_length >= length ) {
 
-				}
+				break;
 
 			}
 
-		},
+		}
 
-		parseAttributes( attributes, maps ) {
+		this.tree.textures.push( texture );
+		this.currentForm = texture;
 
-			var params = {};
+	},
 
-			// don't use color data if color map is present
-			if ( attributes.Color && ! maps.map ) {
+	parseImage() {
 
-				params.color = new THREE.Color().fromArray( attributes.Color.value );
+		this.reader.skip( 8 ); // unknown
+		this.currentForm.fileName = this.reader.getString();
 
-			} else params.color = new THREE.Color();
+	},
 
+	parseXVAL( type, length ) {
 
-			if ( attributes.Transparency && attributes.Transparency.value !== 0 ) {
+		var endOffset = this.reader.offset + length - 4;
+		this.reader.skip( 8 );
 
-				params.opacity = 1 - attributes.Transparency.value;
-				params.transparent = true;
+		this.currentForm[ type ] = this.reader.getFloat32();
 
-			}
+		this.reader.setOffset( endOffset ); // set end offset directly to skip optional envelope
 
-			if ( attributes[ 'Bump Height' ] ) params.bumpScale = attributes[ 'Bump Height' ].value * 0.1;
+	},
 
-			if ( attributes[ 'Refraction Index' ] ) params.refractionRatio = 1 / attributes[ 'Refraction Index' ].value;
+	parseXVAL3( type, length ) {
 
-			this.parseStandardAttributes( params, attributes, maps );
-			this.parsePhongAttributes( params, attributes, maps );
+		var endOffset = this.reader.offset + length - 4;
+		this.reader.skip( 8 );
 
-			return params;
+		this.currentForm[ type ] = {
+			x: this.reader.getFloat32(),
+			y: this.reader.getFloat32(),
+			z: this.reader.getFloat32(),
+		};
 
-		},
+		this.reader.setOffset( endOffset );
 
-		parseStandardAttributes( params, attributes, maps ) {
+	},
 
-			if ( attributes.Luminous && attributes.Luminous.value !== 0 && attributes[ 'Luminous Color' ] ) {
+	// Tags associated with an object
+	// OTAG { type[ID4], tag-string[S0] }
+	parseObjectTag() {
 
-				var emissiveColor = attributes[ 'Luminous Color' ].value.map( function ( val ) {
+		if ( ! this.tree.objectTags ) this.tree.objectTags = {};
 
-					return val * attributes.Luminous.value;
+		this.tree.objectTags[ this.reader.getIDTag() ] = {
+			tagString: this.reader.getString()
+		};
 
-				} );
+	},
 
-				params.emissive = new THREE.Color().fromArray( emissiveColor );
+	// Signals the start of a new layer. All the data chunks which follow will be included in this layer until another layer chunk is encountered.
+	// LAYR: number[U2], flags[U2], pivot[VEC12], name[S0], parent[U2]
+	parseLayer( length ) {
 
-			}
-			if ( attributes.Roughness && ! maps.roughnessMap ) params.roughness = attributes.Roughness.value;
-			if ( attributes.Metallic && ! maps.metalnessMap ) params.metalness = attributes.Metallic.value;
+		var layer = {
+			number: this.reader.getUint16(),
+			flags: this.reader.getUint16(), // If the least significant bit of flags is set, the layer is hidden.
+			pivot: this.reader.getFloat32Array( 3 ), // Note: this seems to be superflous, as the geometry is translated when pivot is present
+			name: this.reader.getString(),
+		};
 
-		},
+		this.tree.layers.push( layer );
+		this.currentLayer = layer;
 
-		parsePhongAttributes( params, attributes, maps ) {
+		var parsedLength = 16 + stringOffset( this.currentLayer.name ); // index ( 2 ) + flags( 2 ) + pivot( 12 ) + stringlength
 
-			if ( attributes.Diffuse ) params.color.multiplyScalar( attributes.Diffuse.value );
+		// if we have not reached then end of the layer block, there must be a parent defined
+		this.currentLayer.parent = ( parsedLength < length ) ? this.reader.getUint16() : - 1; // omitted or -1 for no parent
 
-			if ( attributes.Reflection ) {
+	},
 
-				params.reflectivity = attributes.Reflection.value;
-				params.combine = THREE.AddOperation;
+	// VEC12 * ( F4 + F4 + F4 ) array of x,y,z vectors
+	// Converting from left to right handed coordinate system:
+	// x -> -x and switch material FrontSide -> BackSide
+	parsePoints( length ) {
 
-			}
+		this.currentPoints = [];
+		for ( var i = 0; i < length / 4; i += 3 ) {
 
-			if ( attributes.Luminosity && ! maps.emissiveMap ) params.emissive = new THREE.Color().setScalar( attributes.Luminosity.value );
+			// z -> -z to match three.js right handed coords
+			this.currentPoints.push( this.reader.getFloat32(), this.reader.getFloat32(), - this.reader.getFloat32() );
 
-			if ( attributes.Glossiness !== undefined ) params.shininess = 5 + Math.pow( attributes.Glossiness.value * 7, 6 );
+		}
 
-			// parse specular if there is no roughness - we will interpret the material as 'Phong' in this case
-			if ( ! attributes.Roughness && attributes.Specular && ! maps.specularMap ) params.specular = new THREE.Color().setScalar( attributes.Specular.value * 1.5 );
+	},
 
-		},
+	// parse VMAP or VMAD
+	// Associates a set of floating-point vectors with a set of points.
+	// VMAP: { type[ID4], dimension[U2], name[S0], ( vert[VX], value[F4] # dimension ) * }
 
-		parseEnvMap( connections, maps, attributes ) {
+	// VMAD Associates a set of floating-point vectors with the vertices of specific polygons.
+	// Similar to VMAP UVs, but associates with polygon vertices rather than points
+	// to solve to problem of UV seams:  VMAD chunks are paired with VMAPs of the same name,
+	// if they exist. The vector values in the VMAD will then replace those in the
+	// corresponding VMAP, but only for calculations involving the specified polygons.
+	// VMAD { type[ID4], dimension[U2], name[S0], ( vert[VX], poly[VX], value[F4] # dimension ) * }
+	parseVertexMapping( length, discontinuous ) {
 
-			if ( connections.envMap ) {
+		var finalOffset = this.reader.offset + length;
 
-				var envMap = this.loadTexture( connections.envMap );
+		var channelName = this.reader.getString();
 
-				if ( attributes.transparent && attributes.opacity < 0.999 ) {
+		if ( this.reader.offset === finalOffset ) {
 
-					envMap.mapping = THREE.EquirectangularRefractionMapping;
+			// then we are in a texture node and the VMAP chunk is just a reference to a UV channel name
+			this.currentForm.UVChannel = channelName;
+			return;
 
-					// Reflectivity and refraction mapping don't work well together in Phong materials
-					if ( attributes.reflectivity !== undefined ) {
+		}
 
-						delete attributes.reflectivity;
-						delete attributes.combine;
+		// otherwise reset to initial length and parse normal VMAP CHUNK
+		this.reader.setOffset( this.reader.offset - stringOffset( channelName ) );
+
+		var type = this.reader.getIDTag();
+
+		this.reader.getUint16(); // dimension
+		var name = this.reader.getString();
+
+		var remainingLength = length - 6 - stringOffset( name );
+
+		switch ( type ) {
+
+			case 'TXUV':
+				this.parseUVMapping( name, finalOffset, discontinuous );
+				break;
+			case 'MORF':
+			case 'SPOT':
+				this.parseMorphTargets( name, finalOffset, type ); // can't be discontinuous
+				break;
+			// unsupported VMAPs
+			case 'APSL':
+			case 'NORM':
+			case 'WGHT':
+			case 'MNVW':
+			case 'PICK':
+			case 'RGB ':
+			case 'RGBA':
+				this.reader.skip( remainingLength );
+				break;
+			default:
+				console.warn( 'LWOLoader: unknown vertex map type: ' + type );
+				this.reader.skip( remainingLength );
 
-					}
+		}
 
-					if ( attributes.metalness !== undefined ) {
+	},
 
-						delete attributes.metalness;
+	parseUVMapping( name, finalOffset, discontinuous ) {
 
-					}
+		var uvIndices = [];
+		var polyIndices = [];
+		var uvs = [];
 
-				} else envMap.mapping = THREE.EquirectangularReflectionMapping;
+		while ( this.reader.offset < finalOffset ) {
 
-				maps.envMap = envMap;
+			uvIndices.push( this.reader.getVariableLengthIndex() );
 
-			}
+			if ( discontinuous ) polyIndices.push( this.reader.getVariableLengthIndex() );
 
-		},
+			uvs.push( this.reader.getFloat32(), this.reader.getFloat32() );
 
-		// get texture defined at top level by its index
-		getTexturePathByIndex( index ) {
+		}
 
-			var fileName = '';
+		if ( discontinuous ) {
 
-			if ( ! lwoTree.textures ) return fileName;
+			if ( ! this.currentLayer.discontinuousUVs ) this.currentLayer.discontinuousUVs = {};
 
-			lwoTree.textures.forEach( function ( texture ) {
+			this.currentLayer.discontinuousUVs[ name ] = {
+				uvIndices: uvIndices,
+				polyIndices: polyIndices,
+				uvs: uvs,
+			};
 
-				if ( texture.index === index ) fileName = texture.fileName;
+		} else {
 
-			} );
+			if ( ! this.currentLayer.uvs ) this.currentLayer.uvs = {};
 
-			return fileName;
+			this.currentLayer.uvs[ name ] = {
+				uvIndices: uvIndices,
+				uvs: uvs,
+			};
 
-		},
+		}
 
-		loadTexture( path ) {
+	},
 
-			if ( ! path ) return null;
+	parseMorphTargets( name, finalOffset, type ) {
 
-			return this.textureLoader.load( this.cleanPath( path ) );
+		var indices = [];
+		var points = [];
 
-		},
+		type = ( type === 'MORF' ) ? 'relative' : 'absolute';
 
-		// Lightwave expects textures to be in folder called Images relative
-		// to the model
-		// Otherwise, the full absolute path is stored: D://some_directory/textures/bumpMap.png
-		// In this case, we'll strip out everything and load 'bumpMap.png' from the same directory as the model
-		cleanPath( path ) {
+		while ( this.reader.offset < finalOffset ) {
 
-			if ( path.toLowerCase().indexOf( 'images' ) === 0 ) return './' + path;
-			return path.split( '/' ).pop().split( '\\' ).pop();
+			indices.push( this.reader.getVariableLengthIndex() );
+			// z -> -z to match three.js right handed coords
+			points.push( this.reader.getFloat32(), this.reader.getFloat32(), - this.reader.getFloat32() );
 
-		},
+		}
 
-		// 0 = Reset, 1 = Repeat, 2 = Mirror, 3 = Edge
-		getWrappingType( num ) {
+		if ( ! this.currentLayer.morphTargets ) this.currentLayer.morphTargets = {};
 
-			switch ( num ) {
+		this.currentLayer.morphTargets[ name ] = {
+			indices: indices,
+			points: points,
+			type: type,
+		};
 
-				case 0:
-					console.warn( 'LWOLoader: "Reset" texture wrapping type is not supported in three.js' );
-					return THREE.ClampToEdgeWrapping;
-				case 1: return THREE.RepeatWrapping;
-				case 2: return THREE.MirroredRepeatWrapping;
-				case 3: return THREE.ClampToEdgeWrapping;
+	},
 
-			}
+	// A list of polygons for the current layer.
+	// POLS { type[ID4], ( numvert+flags[U2], vert[VX] # numvert ) * }
+	parsePolygonList( length ) {
 
-		},
+		var finalOffset = this.reader.offset + length;
+		var type = this.reader.getIDTag();
 
-		getType( nodeData ) {
+		var indices = [];
 
-			if ( nodeData.roughness ) return 'Standard';
-			return 'Phong';
+		// hold a list of polygon sizes, to be split up later
+		var polygonDimensions = [];
 
-		},
+		while ( this.reader.offset < finalOffset ) {
 
-	};
+			var numverts = this.reader.getUint16();
 
-	function GeometryParser() {}
+			//var flags = numverts & 64512; // 6 high order bits are flags - ignoring for now
+			numverts = numverts & 1023; // remaining ten low order bits are vertex num
+			polygonDimensions.push( numverts );
 
-	GeometryParser.prototype = {
+			for ( var j = 0; j < numverts; j ++ ) indices.push( this.reader.getVariableLengthIndex() );
 
-		constructor: GeometryParser,
+		}
 
-		parse( geoData, layer ) {
+		var geometryData = {
+			type: type,
+			vertexIndices: indices,
+			polygonDimensions: polygonDimensions,
+			points: this.currentPoints
+		};
 
-			var geometry = new THREE.BufferGeometry();
+		// Note: assuming that all polys will be lines or points if the first is
+		if ( polygonDimensions[ 0 ] === 1 ) geometryData.type = 'points';
+		else if ( polygonDimensions[ 0 ] === 2 ) geometryData.type = 'lines';
 
-			geometry.addAttribute( 'position', new THREE.Float32BufferAttribute( geoData.points, 3 ) );
+		this.currentLayer.geometry = geometryData;
 
-			var indices = this.splitIndices( geoData.vertexIndices, geoData.polygonDimensions );
-			geometry.setIndex( indices );
+	},
 
-			this.parseGroups( geometry, geoData );
+	// Lists the tag strings that can be associated with polygons by the PTAG chunk.
+	// TAGS { tag-string[S0] * }
+	parseTagStrings( length ) {
 
-			geometry.computeVertexNormals();
+		this.tree.tags = this.reader.getStringArray( length );
 
-			this.parseUVs( geometry, layer, indices );
-			this.parseMorphTargets( geometry, layer, indices );
+	},
 
-			// TODO: z may need to be reversed to account for coordinate system change
-			geometry.translate( - layer.pivot[ 0 ], - layer.pivot[ 1 ], - layer.pivot[ 2 ] );
+	// Associates tags of a given type with polygons in the most recent POLS chunk.
+	// PTAG { type[ID4], ( poly[VX], tag[U2] ) * }
+	parsePolygonTagMapping( length ) {
 
-			// var userData = geometry.userData;
-			// geometry = geometry.toNonIndexed()
-			// geometry.userData = userData;
+		var finalOffset = this.reader.offset + length;
+		var type = this.reader.getIDTag();
+		if ( type === 'SURF' ) this.parseMaterialIndices( finalOffset );
+		else { //PART, SMGP, COLR not supported
 
-			return geometry;
+			this.reader.skip( length - 4 );
 
-		},
+		}
 
-		// split quads into tris
-		splitIndices( indices, polygonDimensions ) {
+	},
 
-			var remappedIndices = [];
+	parseMaterialIndices( finalOffset ) {
 
-			var i = 0;
-			polygonDimensions.forEach( function ( dim ) {
+		// array holds polygon index followed by material index
+		this.currentLayer.geometry.materialIndices = [];
 
-				if ( dim < 4 ) {
+		while ( this.reader.offset < finalOffset ) {
 
-					for ( var k = 0; k < dim; k ++ ) remappedIndices.push( indices[ i + k ] );
+			var polygonIndex = this.reader.getVariableLengthIndex();
+			var materialIndex = this.reader.getUint16();
 
-				} else if ( dim === 4 ) {
+			this.currentLayer.geometry.materialIndices.push( polygonIndex, materialIndex );
 
-					remappedIndices.push(
-						indices[ i ],
-						indices[ i + 1 ],
-						indices[ i + 2 ],
+		}
 
-						indices[ i ],
-						indices[ i + 2 ],
-						indices[ i + 3 ]
+	},
 
-					);
+	parseUnknownCHUNK( blockID, length ) {
 
-				} else if ( dim > 4 ) {
+		console.warn( 'LWOLoader: unknown chunk type: ' + blockID + ' length: ' + length );
 
-					for ( var k = 1; k < dim - 1; k ++ ) {
+		// print the chunk plus some bytes padding either side
+		// printBuffer( this.reader.dv.buffer, this.reader.offset - 20, length + 40 );
 
-						remappedIndices.push( indices[ i ], indices[ i + k ], indices[ i + k + 1 ] );
+		var data = this.reader.getString( length );
 
-					}
+		this.currentForm[ blockID ] = data;
 
-					console.warn( 'LWOLoader: polygons with greater than 4 sides are not supported' );
+	}
 
-				}
+};
 
-				i += dim;
+function DataViewReader( buffer ) {
 
-			} );
+	this.dv = new DataView( buffer );
+	this.offset = 0;
 
-			return remappedIndices;
+}
 
-		},
+DataViewReader.prototype = {
 
-		// NOTE: currently ignoring poly indices and assuming that they are intelligently ordered
-		parseGroups( geometry, geoData ) {
+	constructor: DataViewReader,
 
-			var tags = lwoTree.tags;
-			var matNames = [];
+	size: function () {
 
-			var elemSize = 3;
-			if ( geoData.type === 'lines' ) elemSize = 2;
-			if ( geoData.type === 'points' ) elemSize = 1;
+		return this.dv.buffer.byteLength;
 
-			var remappedIndices = this.splitMaterialIndices( geoData.polygonDimensions, geoData.materialIndices );
+	},
 
-			var indexNum = 0; // create new indices in numerical order
-			var indexPairs = {}; // original indices mapped to numerical indices
+	setOffset( offset ) {
 
-			var prevMaterialIndex;
+		if ( offset > 0 && offset < this.dv.buffer.byteLength ) {
 
-			var prevStart = 0;
-			var currentCount = 0;
+			this.offset = offset;
 
-			for ( var i = 0; i < remappedIndices.length; i += 2 ) {
+		} else {
 
-				var materialIndex = remappedIndices[ i + 1 ];
+			console.error( 'LWOLoader: invalid buffer offset' );
 
-				if ( i === 0 ) matNames[ indexNum ] = tags[ materialIndex ];
+		}
 
-				if ( prevMaterialIndex === undefined ) prevMaterialIndex = materialIndex;
+	},
 
-				if ( materialIndex !== prevMaterialIndex ) {
+	endOfFile: function () {
 
-					var currentIndex;
-					if ( indexPairs[ tags[ prevMaterialIndex ] ] ) {
+		if ( this.offset >= this.size() ) return true;
+		return false;
 
-						currentIndex = indexPairs[ tags[ prevMaterialIndex ] ];
+	},
 
-					} else {
+	skip: function ( length ) {
 
-						currentIndex = indexNum;
-						indexPairs[ tags[ prevMaterialIndex ] ] = indexNum;
-						matNames[ indexNum ] = tags[ prevMaterialIndex ];
-						indexNum ++;
+		this.offset += length;
 
-					}
+	},
 
-					geometry.addGroup( prevStart, currentCount, currentIndex );
+	getUint8: function () {
 
-					prevStart += currentCount;
+		var value = this.dv.getUint8( this.offset );
+		this.offset += 1;
+		return value;
 
-					prevMaterialIndex = materialIndex;
-					currentCount = 0;
+	},
 
-				}
+	getUint16: function () {
 
-				currentCount += elemSize;
+		var value = this.dv.getUint16( this.offset );
+		this.offset += 2;
+		return value;
 
-			}
+	},
 
-			// the loop above doesn't add the last group, do that here.
-			if ( geometry.groups.length > 0 ) {
+	getInt32: function () {
 
-				var currentIndex;
-				if ( indexPairs[ tags[ materialIndex ] ] ) {
+		var value = this.dv.getInt32( this.offset, false );
+		this.offset += 4;
+		return value;
 
-					currentIndex = indexPairs[ tags[ materialIndex ] ];
+	},
 
-				} else {
+	getUint32: function () {
 
-					currentIndex = indexNum;
-					indexPairs[ tags[ materialIndex ] ] = indexNum;
-					matNames[ indexNum ] = tags[ materialIndex ];
+		var value = this.dv.getUint32( this.offset, false );
+		this.offset += 4;
+		return value;
 
-				}
+	},
 
-				geometry.addGroup( prevStart, currentCount, currentIndex );
+	getUint64: function () {
 
-			}
+		var low, high;
 
-			// Mat names from TAGS chunk, used to build up an array of materials for this geometry
-			geometry.userData.matNames = matNames;
+		high = this.getUint32();
+		low = this.getUint32();
+		return high * 0x100000000 + low;
 
-		},
+	},
 
-		splitMaterialIndices( polygonDimensions, indices ) {
+	getFloat32: function () {
 
-			var remappedIndices = [];
+		var value = this.dv.getFloat32( this.offset, false );
+		this.offset += 4;
+		return value;
 
-			polygonDimensions.forEach( function ( dim, i ) {
+	},
 
-				if ( dim <= 3 ) {
+	getFloat32Array: function ( size ) {
 
-					remappedIndices.push( indices[ i * 2 ], indices[ i * 2 + 1 ] );
+		var a = [];
 
-				} else if ( dim === 4 ) {
+		for ( var i = 0; i < size; i ++ ) {
 
-					remappedIndices.push( indices[ i * 2 ], indices[ i * 2 + 1 ], indices[ i * 2 ], indices[ i * 2 + 1 ] );
+			a.push( this.getFloat32() );
 
-				} else {
+		}
 
-					 // ignore > 4 for now
-					for ( var k = 0; k < dim - 2; k ++ ) {
+		return a;
 
-						remappedIndices.push( indices[ i * 2 ], indices[ i * 2 + 1 ] );
+	},
 
-					}
+	getFloat64: function () {
 
-				}
+		var value = this.dv.getFloat64( this.offset, this.littleEndian );
+		this.offset += 8;
+		return value;
 
-			} );
+	},
 
-			return remappedIndices;
+	getFloat64Array: function ( size ) {
 
-		},
+		var a = [];
 
-		// UV maps:
-		// 1: are defined via index into an array of points, not into a geometry
-		// - the geometry is also defined by an index into this array, but the indexes may not match
-		// 2: there can be any number of UV maps for a single geometry. Here these are combined,
-		// 	with preference given to the first map encountered
-		// 3: UV maps can be partial - that is, defined for only a part of the geometry
-		// 4: UV maps can be VMAP or VMAD (discontinuous, to allow for seams). In practice, most
-		// UV maps are defined as partially VMAP and partially VMAD
-		// VMADs are currently not supported
-		parseUVs( geometry, layer ) {
+		for ( var i = 0; i < size; i ++ ) {
 
-			// start by creating a UV map set to zero for the whole geometry
-			var remappedUVs = Array.from( Array( geometry.attributes.position.count * 2 ), function () {
+			a.push( this.getFloat64() );
 
-				return 0;
+		}
 
-			} );
+		return a;
 
-			for ( var name in layer.uvs ) {
+	},
 
-				var uvs = layer.uvs[ name ].uvs;
-				var uvIndices = layer.uvs[ name ].uvIndices;
+	// get variable-length index data type
+	// VX ::= index[U2] | (index + 0xFF000000)[U4]
+	// If the index value is less than 65,280 (0xFF00),then VX === U2
+	// otherwise VX === U4 with bits 24-31 set
+	// When reading an index, if the first byte encountered is 255 (0xFF), then
+	// the four-byte form is being used and the first byte should be discarded or masked out.
+	getVariableLengthIndex() {
 
-				uvIndices.forEach( function ( i, j ) {
+		var firstByte = this.getUint8();
 
-					remappedUVs[ i * 2 ] = uvs[ j * 2 ];
-					remappedUVs[ i * 2 + 1 ] = uvs[ j * 2 + 1 ];
+		if ( firstByte === 255 ) {
 
-				} );
+			return this.getUint8() * 65536 + this.getUint8() * 256 + this.getUint8();
 
-			}
+		}
 
-			geometry.addAttribute( 'uv', new THREE.Float32BufferAttribute( remappedUVs, 2 ) );
+		return firstByte * 256 + this.getUint8();
 
-		},
+	},
 
-		parseMorphTargets( geometry, layer ) {
+	// An ID tag is a sequence of 4 bytes containing 7-bit ASCII values
+	getIDTag() {
 
-			var num = 0;
-			for ( var name in layer.morphTargets ) {
+		return this.getString( 4 );
 
-				var remappedPoints = geometry.attributes.position.array.slice();
+	},
 
-				if ( ! geometry.morphAttributes.position ) geometry.morphAttributes.position = [];
+	getString: function ( size ) {
 
-				var morphPoints = layer.morphTargets[ name ].points;
-				var morphIndices = layer.morphTargets[ name ].indices;
-				var type = layer.morphTargets[ name ].type;
+		if ( size === 0 ) return;
 
-				morphIndices.forEach( function ( i, j ) {
+		// note: safari 9 doesn't support Uint8Array.indexOf; create intermediate array instead
+		var a = [];
 
-					if ( type === 'relative' ) {
+		if ( size ) {
 
-						remappedPoints[ i * 3 ] += morphPoints[ j * 3 ];
-						remappedPoints[ i * 3 + 1 ] += morphPoints[ j * 3 + 1 ];
-						remappedPoints[ i * 3 + 2 ] += morphPoints[ j * 3 + 2 ];
+			for ( var i = 0; i < size; i ++ ) {
 
-					} else {
+				a[ i ] = this.getUint8();
 
-						remappedPoints[ i * 3 ] = morphPoints[ j * 3 ];
-						remappedPoints[ i * 3 + 1 ] = morphPoints[ j * 3 + 1 ];
-						remappedPoints[ i * 3 + 2 ] = morphPoints[ j * 3 + 2 ];
+			}
 
-					}
+		} else {
 
-				} );
+			var currentChar;
+			var len = 0;
 
-				geometry.morphAttributes.position[ num ] = new THREE.Float32BufferAttribute( remappedPoints, 3 );
-				geometry.morphAttributes.position[ num ].name = name;
+			while ( currentChar !== 0 ) {
 
-				num ++;
+				currentChar = this.getUint8();
+				if ( currentChar !== 0 ) a.push( currentChar );
+				len ++;
 
 			}
 
-		},
-
-	};
-
-	// parse data from the IFF buffer.
-	// LWO3 files are in IFF format and can contain the following data types, referred to by shorthand codes
-	//
-	// ATOMIC DATA TYPES
-	// ID Tag - 4x 7 bit uppercase ASCII chars: ID4
-	// signed integer, 1, 2, or 4 byte length: I1, I2, I4
-	// unsigned integer, 1, 2, or 4 byte length: U1, U2, U4
-	// float, 4 byte length: F4
-	// string, series of ASCII chars followed by null byte (If the length of the string including the null terminating byte is odd, an extra null is added so that the data that follows will begin on an even byte boundary): S0
-	//
-	//  COMPOUND DATA TYPES
-	// Variable-length Index (index into an array or collection): U2 or U4 : VX
-	// Color (RGB): F4 + F4 + F4: COL12
-	// Coordinate (x, y, z): F4 + F4 + F4: VEC12
-	// Percentage F4 data type from 0->1 with 1 = 100%: FP4
-	// Angle in radian F4: ANG4
-	// Filename (string) S0: FNAM0
-	// XValue F4 + index (VX) + optional envelope( ENVL ): XVAL
-	// XValue vector VEC12 + index (VX) + optional envelope( ENVL ): XVAL3
-	//
-	// The IFF file is arranged in chunks:
-	// CHUNK = ID4 + length (U4) + length X bytes of data + optional 0 pad byte
-	// optional 0 pad byte is there to ensure chunk ends on even boundary, not counted in size
-
-	// Chunks are combined in Forms (collections of chunks)
-	// FORM = string 'FORM' (ID4) + length (U4) + type (ID4) + optional ( CHUNK | FORM )
-
-	// CHUNKS and FORMS are collectively referred to as blocks
-
-	// The entire file is contained in one top level FORM
-	function IFFParser() {}
-
-	IFFParser.prototype = {
-
-		constructor: IFFParser,
-
-		parse: function ( buffer ) {
-
-			// dump the whole buffer as a string for testing
-			// printBuffer( buffer );
-
-			this.reader = new DataViewReader( buffer );
-
-			this.tree = {
-				materials: {},
-				layers: [],
-				tags: [],
-				textures: [],
-			};
+			if ( ! isEven( len + 1 ) ) this.getUint8(); // if string with terminating nullbyte is uneven, extra nullbyte is added
 
-			// start out at the top level to add any data before first layer is encountered
-			this.currentLayer = this.tree;
-			this.currentForm = this.tree;
+		}
 
-			// parse blocks until end of file is reached
-			while ( ! this.reader.endOfFile() ) this.parseBlock();
+		return THREE.LoaderUtils.decodeText( new Uint8Array( a ) );
 
-			return this.tree;
+	},
 
-		},
+	getStringArray: function ( size ) {
 
-		parseBlock() {
+		var a = this.getString( size );
+		a = a.split( '\0' );
 
-			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 ) {
+		return a.filter( Boolean ); // return array with any empty strings removed
 
-				this.reader.offset -= 4;
-				length = this.reader.getUint16();
+	}
 
-			}
+};
 
+// ************** DEBUGGER  **************
 
-			// Data types may be found in either LWO2 OR LWO3 spec
-			switch ( blockID ) {
+function Debugger( ) {
 
-				case 'FORM': // form blocks may consist of sub -chunks or sub-forms
-					this.parseForm( length );
-					break;
+	this.active = false;
+	this.depth = 0;
+	this.formList = [];
 
-					// SKIPPED CHUNKS
-
-				// MISC skipped
-				case 'ICON': // Thumbnail Icon Image
-				case 'VMPA': // Vertex Map Parameter
-				case 'BBOX': // bounding box
-				// case 'VMMD':
-				// case 'VTYP':
-
-				// normal maps can be specified, normally on models imported from other applications. Currently ignored
-				case 'NORM':
-
-				// ENVL FORM skipped
-				case 'PRE ':
-				case 'POST':
-				case 'KEY ':
-				case 'SPAN':
-
-				// CLIP FORM skipped
-				case 'TIME':
-				case 'CLRS':
-				case 'CLRA':
-				case 'FILT':
-				case 'DITH':
-				case 'CONT':
-				case 'BRIT':
-				case 'SATR':
-				case 'HUE ':
-				case 'GAMM':
-				case 'NEGA':
-				case 'IFLT':
-				case 'PFLT':
-
-				// Image Map Layer skipped
-				case 'PROJ':
-				case 'AXIS':
-				case 'AAST':
-				case 'PIXB':
-				case 'STCK':
-
-				// Procedural Textures skipped
-				case 'VALU':
-
-				// Gradient Textures skipped
-				case 'PNAM':
-				case 'INAM':
-				case 'GRST':
-				case 'GREN':
-				case 'GRPT':
-				case 'FKEY':
-				case 'IKEY':
-
-				// Texture Mapping Form skipped
-				case 'CSYS':
-
-					// Surface CHUNKs skipped
-				case 'OPAQ': // top level 'opacity' checkbox
-				case 'CMAP': // clip map
-
-				// Surface node CHUNKS skipped
-				// These mainly specify the node editor setup in LW
-				case 'NLOC':
-				case 'NZOM':
-				case 'NVER':
-				case 'NSRV':
-				case 'NCRD':
-				case 'NMOD':
-				case 'NPRW':
-				case 'NPLA':
-				case 'VERS':
-				case 'ENUM':
-				case 'TAG ':
-
-				// Car Material CHUNKS
-				case 'CGMD':
-				case 'CGTY':
-				case 'CGST':
-				case 'CGEN':
-				case 'CGTS':
-				case 'CGTE':
-				case 'OSMP':
-				case 'OMDE':
-				case 'OUTR':
-					this.reader.skip( length );
-					break;
+}
 
-				case 'FLAG':
-					if ( this.tree.format === 'LWO2' ) {
+Debugger.prototype = {
 
-						this.reader.skip( 4 ); // not suported
+	constructor: Debugger,
 
-					} else {
+	enable: function () {
 
-						this.reader.skip( length );
+		this.active = true;
 
-					}
-					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':
-				case 'SHRP':
-				case 'RFOP':
-				case 'RSAN':
-				case 'TROP':
-				case 'RBLR':
-				case 'TBLR':
-				case 'CLRH':
-				case 'CLRF':
-				case 'ADTR':
-				case 'GLOW':
-				case 'LINE':
-				case 'ALPH':
-				case 'VCOL':
-				case 'ENAB':
-					this.reader.skip( length );
-					break;
-				case 'SURF':
-					if ( this.tree.format === 'LWO2' ) {
+	},
 
-						this.parseSurfaceLwo2( length );
+	log: function () {
 
-					}
-					break;
-				case 'CLIP':
-					if ( this.tree.format === 'LWO2' ) {
+		if ( ! this.active ) return;
 
-						this.parseClipLwo2( length );
+		var nodeType;
 
-					}
-					break;
-				// Texture node chunks (not in spec)
-				case 'IPIX': // usePixelBlending
-				case 'IMIP': // useMipMaps
-				case 'IMOD': // imageBlendingMode
-				case 'AMOD': // unknown
-				case 'IINV': // imageInvertAlpha
-				case 'INCR': // imageInvertColor
-				case 'IAXS': // imageAxis ( for non-UV maps)
-				case 'IFOT': // imageFallofType
-				case 'ITIM': // timing for animated textures
-				case 'IWRL':
-				case 'IUTI':
-				case 'IINX':
-				case 'IINY':
-				case 'IINZ':
-				case 'IREF': // possibly a VX for reused texture nodes
-					if ( length === 4 ) this.currentNode[ blockID ] = this.reader.getInt32();
-					else this.reader.skip( length );
-					break;
+		switch ( this.node ) {
 
-				case 'OTAG':
-					this.parseObjectTag();
-					break;
+			case 0:
+				nodeType = "FORM";
+				break;
 
-				case 'LAYR':
-					this.parseLayer( length );
-					break;
+			case 1:
+				nodeType = "CHK";
+				break;
 
-				case 'PNTS':
-					this.parsePoints( length );
-					break;
+			case 2:
+				nodeType = "S-CHK";
+				break;
 
-				case 'VMAP':
-					this.parseVertexMapping( length );
-					break;
+		}
 
-				case 'POLS':
-					this.parsePolygonList( length );
-					break;
+		console.log(
+			"| ".repeat( this.depth ) +
+			nodeType,
+			this.nodeID,
+			`( ${this.offset} ) -> ( ${this.dataOffset + this.length} )`,
+			( ( this.node == 0 ) ? " {" : "" ),
+			( ( this.skipped ) ? "SKIPPED" : "" ),
+			( ( this.node == 0 && this.skipped ) ? "}" : "" )
+		);
 
-				case 'TAGS':
-					this.parseTagStrings( length );
-					break;
+		if ( this.node == 0 && ! this.skipped ) {
 
-				case 'PTAG':
-					this.parsePolygonTagMapping( length );
-					break;
+			this.depth += 1;
+			this.formList.push( this.dataOffset + this.length );
 
-				case 'VMAD':
-					this.parseVertexMapping( length, true );
-					break;
+		}
 
-				// Misc CHUNKS
-				case 'DESC': // Description Line
-					this.currentForm.description = this.reader.getString();
-					break;
+		this.skipped = false;
 
-				case 'TEXT':
-				case 'CMNT':
-				case 'NCOM':
-					this.currentForm.comment = this.reader.getString();
-					break;
+	},
 
-					// Envelope Form
-				case 'NAME':
-					this.currentForm.channelName = this.reader.getString();
-					break;
+	closeForms: function () {
 
-					// Image Map Layer
+		if ( ! this.active ) return;
 
-				case 'WRAP':
-					this.currentForm.wrap = { w: this.reader.getUint16(), h: this.reader.getUint16() };
-					break;
+		for ( var i = this.formList.length - 1; i >= 0; i -- ) {
 
-				case 'IMAG':
-					var index = this.reader.getVariableLengthIndex();
-					this.currentForm.imageIndex = index;
-					break;
+			if ( this.offset >= this.formList[ i ] ) {
 
-					// Texture Mapping Form
+				this.depth -= 1;
+				console.log( "| ".repeat( this.depth ) + "}" );
+				this.formList.splice( - 1, 1 );
 
-				case 'OREF':
-					this.currentForm.referenceObject = this.reader.getString();
-					break;
+			}
 
-				case 'ROID':
-					this.currentForm.referenceObjectID = this.reader.getUint32();
-					break;
+		}
 
-					// Surface Blocks
+	}
 
-				case 'SSHN':
-					this.currentSurface.surfaceShaderName = this.reader.getString();
-					break;
+};
 
-				case 'AOVN':
-					this.currentSurface.surfaceCustomAOVName = this.reader.getString();
-					break;
+// ************** UTILITY FUNCTIONS **************
 
-					// Nodal Blocks
+function isEven( num ) {
 
-				case 'NSTA':
-					this.currentForm.disabled = this.reader.getUint16();
-					break;
+	return num % 2;
 
-				case 'NRNM':
-					this.currentForm.realName = this.reader.getString();
-					break;
+}
 
-				case 'NNME':
-					this.currentForm.refName = this.reader.getString();
-					this.currentSurface.nodes[ this.currentForm.refName ] = this.currentForm;
-					break;
+// calculate the length of the string in the buffer
+// this will be string.length + nullbyte + optional padbyte to make the length even
+function stringOffset( string ) {
 
-				// Nodal Blocks : connections
-				case 'INME':
-					if ( ! this.currentForm.nodeName ) this.currentForm.nodeName = [];
-					this.currentForm.nodeName.push( this.reader.getString() );
-					break;
+	return string.length + 1 + ( isEven( string.length + 1 ) ? 1 : 0 );
 
-				case 'IINN':
-					if ( ! this.currentForm.inputNodeName ) this.currentForm.inputNodeName = [];
-					this.currentForm.inputNodeName.push( this.reader.getString() );
-					break;
+}
 
-				case 'IINM':
-					if ( ! this.currentForm.inputName ) this.currentForm.inputName = [];
-					this.currentForm.inputName.push( this.reader.getString() );
-					break;
+// for testing purposes, dump buffer to console
+// printBuffer( this.reader.dv.buffer, this.reader.offset, length );
+function printBuffer( buffer, from, to ) {
 
-				case 'IONM':
-					if ( ! this.currentForm.inputOutputName ) this.currentForm.inputOutputName = [];
-					this.currentForm.inputOutputName.push( this.reader.getString() );
-					break;
+	console.log( THREE.LoaderUtils.decodeText( new Uint8Array( buffer, from, to ) ) );
 
-				case 'FNAM':
-					this.currentForm.fileName = this.reader.getString();
-					break;
+}
 
-				case 'CHAN': // NOTE: ENVL Forms may also have CHAN chunk, however ENVL is currently ignored
-					if ( length === 4 ) this.currentForm.textureChannel = this.reader.getIDTag();
-					else this.reader.skip( length );
-					break;
+var lwoTree;
 
-					// LWO2 Spec chunks: these are needed since the SURF FORMs are often in LWO2 format
+THREE.LWOLoader = function ( manager, parameters ) {
 
-				case 'SMAN':
-					var maxSmoothingAngle = this.reader.getFloat32();
-					this.currentSurface.attributes.smooth = ( maxSmoothingAngle < 0 ) ? false : true;
-					break;
+	this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
 
-				// LWO2: Basic Surface Parameters
-				case 'COLR':
-					this.currentSurface.attributes.Color = {};
-					this.currentSurface.attributes.Color.value = this.reader.getFloat32Array( 3 );
-					this.reader.skip( 2 ); // VX: envelope
-					break;
+	parameters = parameters || {};
 
-				case 'LUMI':
-					this.currentSurface.attributes.luminosityLevel = this.reader.getFloat32();
-					this.reader.skip( 2 );
-					break;
+	this.resourcePath = ( parameters.resourcePath !== undefined ) ? parameters.resourcePath : undefined;
 
-				case 'SPEC':
-					this.currentSurface.attributes.specularLevel = this.reader.getFloat32();
-					this.reader.skip( 2 );
-					break;
+};
 
-				case 'TRAN':
-					this.currentSurface.attributes.opacity = this.reader.getFloat32();
-					this.reader.skip( 2 );
-					break;
+THREE.LWOLoader.prototype = {
 
-				case 'BUMP':
-					this.currentSurface.attributes.bumpStrength = this.reader.getFloat32();
-					this.reader.skip( 2 );
-					break;
+	constructor: THREE.LWOLoader,
 
-				case 'SIDE':
-					this.currentSurface.attributes.side = this.reader.getUint16();
-					break;
+	crossOrigin: 'anonymous',
 
-				case 'RIMG':
-					this.currentSurface.attributes.reflectionMap = this.reader.getVariableLengthIndex();
-					break;
+	load: function ( url, onLoad, onProgress, onError ) {
 
-				case 'RIND':
-					this.currentSurface.attributes.refractiveIndex = this.reader.getFloat32();
-					this.reader.skip( 2 );
-					break;
+		var self = this;
 
-				case 'TIMG':
-					this.currentSurface.attributes.refractionMap = this.reader.getVariableLengthIndex();
-					break;
+		var path = ( self.path === undefined ) ? extractParentUrl( url, 'Objects' ) : self.path;
 
-				case 'IMAP':
-					if ( this.tree.format === 'LWO2' ) {
+		// give the mesh a default name based on the filename
+		var modelName = url.split( path ).pop().split( '.' )[ 0 ];
 
-						this.reader.skip( 2 );
+		var loader = new THREE.FileLoader( this.manager );
+		loader.setPath( self.path );
+		loader.setResponseType( 'arraybuffer' );
 
-					} else {
+		loader.load( url, function ( buffer ) {
 
-						this.currentSurface.attributes.imageMapIndex = this.reader.getUint32();
+			// console.time( 'Total parsing: ' );
+			onLoad( self.parse( buffer, path, modelName ) );
+			// console.timeEnd( 'Total parsing: ' );
 
-					}
-					break;
+		}, onProgress, onError );
 
-				case 'IUVI': // uv channel name
-					this.currentNode.UVChannel = this.reader.getString( length );
-					break;
+	},
 
-				case 'IUTL': // widthWrappingMode: 0 = Reset, 1 = Repeat, 2 = Mirror, 3 = Edge
-					this.currentNode.widthWrappingMode = this.reader.getUint32();
-					break;
-				case 'IVTL': // heightWrappingMode
-					this.currentNode.heightWrappingMode = this.reader.getUint32();
-					break;
+	setCrossOrigin: function ( value ) {
 
-				// LWO2 USE
-				case 'BLOK':
-					// skip
-					break;
+		this.crossOrigin = value;
+		return this;
 
-				default:
-					this.parseUnknownCHUNK( blockID, length );
+	},
 
-			}
+	setPath: function ( value ) {
 
-			if ( this.reader.offset >= this.currentFormEnd ) {
+		this.path = value;
+		return this;
 
-				this.currentForm = this.parentForm;
+	},
 
-			}
+	setResourcePath: function ( value ) {
 
-		},
-
-
-		///
-		// FORM PARSING METHODS
-		///
-
-		// Forms are organisational and can contain any number of sub chunks and sub forms
-		// FORM ::= 'FORM'[ID4], length[U4], type[ID4], ( chunk[CHUNK] | form[FORM] ) * }
-		parseForm( length ) {
-
-			var type = this.reader.getIDTag();
-
-			switch ( type ) {
-
-				// SKIPPED FORMS
-				// if skipForm( length ) is called, the entire form and any sub forms and chunks are skipped
-
-				case 'ISEQ': // Image sequence
-				case 'ANIM': // plug in animation
-				case 'STCC': // Color-cycling Still
-				case 'VPVL':
-				case 'VPRM':
-				case 'NROT':
-				case 'WRPW': // image wrap w ( for cylindrical and spherical projections)
-				case 'WRPH': // image wrap h
-				case 'FUNC':
-				case 'FALL':
-				case 'OPAC':
-				case 'GRAD': // gradient texture
-				case 'ENVS':
-				case 'VMOP':
-				case 'VMBG':
-
-				// Car Material FORMS
-				case 'OMAX':
-				case 'STEX':
-				case 'CKBG':
-				case 'CKEY':
-				case 'VMLA':
-				case 'VMLB':
-					this.skipForm( length ); // not currently supported
-					break;
+		this.resourcePath = value;
+		return this;
 
-				// if break; is called directly, the position in the lwoTree is not created
-				// any sub chunks and forms are added to the parent form instead
-				case 'META':
-				case 'NNDS':
-				case 'NODS':
-				case 'NDTA':
-				case 'ADAT':
-				case 'AOVS':
-				case 'BLOK':
-
-				// used by texture nodes
-				case 'IBGC': // imageBackgroundColor
-				case 'IOPC': // imageOpacity
-				case 'IIMG': // hold reference to image path
-				case 'TXTR':
-					// this.setupForm( type, length );
-					break;
+	},
 
-				case 'IFAL': // imageFallof
-				case 'ISCL': // imageScale
-				case 'IPOS': // imagePosition
-				case 'IROT': // imageRotation
-				case 'IBMP':
-				case 'IUTD':
-				case 'IVTD':
-					this.parseTextureNodeAttribute( type );
-					break;
+	parse: function ( iffBuffer, path, modelName ) {
 
-				case 'LWO2':
-					this.tree.format = type;
-					break;
+		lwoTree = new IFFParser().parse( iffBuffer );
 
-				case 'LWO3':
-					this.tree.format = type;
-					break;
+		// console.log( 'lwoTree', lwoTree );
 
-				case 'ENVL':
-					this.parseEnvelope( length );
-					break;
+		var textureLoader = new THREE.TextureLoader( this.manager ).setPath( this.resourcePath || path ).setCrossOrigin( this.crossOrigin );
 
-					// CLIP FORM AND SUB FORMS
+		return new LWOTreeParser( textureLoader ).parse( modelName );
 
-				case 'CLIP':
-					if ( this.tree.format === 'LWO2' ) {
+	}
 
-						this.parseForm( length );
+};
 
-					} else {
+// Parse the lwoTree object
+function LWOTreeParser( textureLoader ) {
 
-						this.parseClip( length );
+	this.textureLoader = textureLoader;
 
-					}
-					break;
+}
 
-				case 'STIL':
-					this.parseImage();
-					break;
+LWOTreeParser.prototype = {
 
-				case 'XREF': // clone of another STIL
-					this.reader.skip( 8 ); // unknown
-					this.currentForm.referenceTexture = {
-						index: this.reader.getUint32(),
-						refName: this.reader.getString() // internal unique ref
-					};
-					break;
+	constructor: LWOTreeParser,
 
-					// Not in spec, used by texture nodes
+	parse: function ( modelName ) {
 
-				case 'IMST':
-					this.parseImageStateForm( length );
-					break;
+		this.materials = new MaterialParser( this.textureLoader ).parse();
+		this.defaultLayerName = modelName;
 
-					// SURF FORM AND SUB FORMS
+		this.meshes = this.parseLayers();
 
-				case 'SURF':
-					this.parseSurfaceForm( length );
-					break;
+		return {
+			materials: this.materials,
+			meshes: this.meshes,
+		};
 
-				case 'VALU': // Not in spec
-					this.parseValueForm( length );
-					break;
+	},
 
-				case 'NTAG':
-					this.parseSubNode( length );
-					break;
+	parseLayers() {
 
-				case 'ATTR': // BSDF Node Attributes
-				case 'SATR': // Standard Node Attributes
-					this.setupForm( 'attributes', length );
-					break;
+		// array of all meshes for building hierarchy
+		var meshes = [];
 
-				case 'NCON':
-					this.parseConnections( length );
-					break;
+		// final array containing meshes with scene graph hierarchy set up
+		var finalMeshes = [];
 
-				case 'SSHA':
-					this.parentForm = this.currentForm;
-					this.currentForm = this.currentSurface;
-					this.setupForm( 'surfaceShader', length );
-					break;
+		var geometryParser = new GeometryParser();
 
-				case 'SSHD':
-					this.setupForm( 'surfaceShaderData', length );
-					break;
+		var self = this;
+		lwoTree.layers.forEach( function ( layer ) {
 
-				case 'ENTR': // Not in spec
-					this.parseEntryForm( length );
-					break;
+			var geometry = geometryParser.parse( layer.geometry, layer );
 
-					// Image Map Layer
+			var mesh = self.parseMesh( geometry, layer );
 
-				case 'IMAP':
-					this.parseImageMap( length );
-					break;
+			meshes[ layer.number ] = mesh;
 
-				case 'TAMP':
-					this.parseXVAL( 'amplitude', length );
-					break;
+			if ( layer.parent === - 1 ) finalMeshes.push( mesh );
+			else meshes[ layer.parent ].add( mesh );
 
-					//Texture Mapping Form
 
-				case 'TMAP':
-					this.setupForm( 'textureMap', length );
-					break;
+		} );
 
-				case 'CNTR':
-					this.parseXVAL3( 'center', length );
-					break;
+		this.applyPivots( finalMeshes );
 
-				case 'SIZE':
-					this.parseXVAL3( 'scale', length );
-					break;
+		return finalMeshes;
 
-				case 'ROTA':
-					this.parseXVAL3( 'rotation', length );
-					break;
+	},
 
-				default:
-					this.parseUnknownForm( type, length );
+	parseMesh( geometry, layer ) {
 
-			}
+		var mesh;
 
-		},
+		var materials = this.getMaterials( geometry.userData.matNames, layer.geometry.type );
 
-		setupForm( type, length ) {
+		this.duplicateUVs( geometry, materials );
 
-			if ( ! this.currentForm ) this.currentForm = this.currentNode;
+		if ( layer.geometry.type === 'points' ) mesh = new THREE.Points( geometry, materials );
+		else if ( layer.geometry.type === 'lines' ) mesh = new THREE.LineSegments( geometry, materials );
+		else mesh = new THREE.Mesh( geometry, materials );
 
-			this.currentFormEnd = this.reader.offset + length;
-			this.parentForm = this.currentForm;
+		if ( layer.name ) mesh.name = layer.name;
+		else mesh.name = this.defaultLayerName + '_layer_' + layer.number;
 
-			if ( ! this.currentForm[ type ] ) {
+		mesh.userData.pivot = layer.pivot;
 
-				this.currentForm[ type ] = {};
-				this.currentForm = this.currentForm[ type ];
+		return mesh;
 
+	},
 
-			} else {
+	// TODO: may need to be reversed in z to convert LWO to three.js coordinates
+	applyPivots( meshes ) {
 
-				// should never see this unless there's a bug in the reader
-				console.warn( 'LWOLoader: form already exists on parent: ', type, this.currentForm );
+		meshes.forEach( function ( mesh ) {
 
-				this.currentForm = this.currentForm[ type ];
+			mesh.traverse( function ( child ) {
 
-			}
+				var pivot = child.userData.pivot;
 
+				child.position.x += pivot[ 0 ];
+				child.position.y += pivot[ 1 ];
+				child.position.z += pivot[ 2 ];
 
-		},
+				if ( child.parent ) {
 
-		skipForm( length ) {
+					var parentPivot = child.parent.userData.pivot;
 
-			this.reader.skip( length - 4 );
+					child.position.x -= parentPivot[ 0 ];
+					child.position.y -= parentPivot[ 1 ];
+					child.position.z -= parentPivot[ 2 ];
 
-		},
+				}
 
-		parseUnknownForm( type, length ) {
+			} );
 
-			console.warn( 'LWOLoader: unknown FORM encountered: ' + type, length );
+		} );
 
-			printBuffer( this.reader.dv.buffer, this.reader.offset, length - 4 );
-			this.reader.skip( length - 4 );
+	},
 
-		},
+	getMaterials( namesArray, type ) {
 
-		parseSurfaceForm( length ) {
+		var materials = [];
 
-			this.reader.skip( 8 ); // unknown Uint32 x2
+		var self = this;
 
-			var name = this.reader.getString();
+		namesArray.forEach( function ( name, i ) {
 
-			var surface = {
-				attributes: {}, // LWO2 style non-node attributes will go here
-				connections: {},
-				name: name,
-				inputName: name,
-				nodes: {},
-				source: this.reader.getString(),
-			};
+			materials[ i ] = self.getMaterialByName( name );
 
-			this.tree.materials[ name ] = surface;
-			this.currentSurface = surface;
+		} );
 
-			this.parentForm = this.tree.materials;
-			this.currentForm = surface;
-			this.currentFormEnd = this.reader.offset + length;
+		// convert materials to line or point mats if required
+		if ( type === 'points' || type === 'lines' ) {
 
-		},
+			materials.forEach( function ( mat, i ) {
 
-		parseSurfaceLwo2( length ) {
+				var spec = {
+					color: mat.color,
+				};
 
-			var firstOffset = this.reader.offset;
-			var name = this.reader.getString();
+				if ( type === 'points' ) {
 
-			var surface = {
-				attributes: {}, // LWO2 style non-node attributes will go here
-				connections: {},
-				name: name,
-				nodes: {},
-				source: this.reader.getString(),
-			};
+					spec.size = 0.1;
+					spec.map = mat.map;
+					spec.morphTargets = mat.morphTargets;
+					materials[ i ] = new THREE.PointsMaterial( spec );
 
-			this.tree.materials[ name ] = surface;
-			this.currentSurface = surface;
+				} else if ( type === 'lines' ) {
 
-			this.parentForm = this.tree.materials;
-			this.currentForm = surface;
-			this.currentFormEnd = this.reader.offset + length;
+					materials[ i ] = new THREE.LineBasicMaterial( spec );
 
-		},
+				}
 
-		parseSubNode( length ) {
+			} );
 
-			// parse the NRNM CHUNK of the subnode FORM to get
-			// a meaningful name for the subNode
-			// some subnodes can be renamed, but Input and Surface cannot
+		}
 
-			this.reader.skip( 8 ); // NRNM + length
-			var name = this.reader.getString();
+		// if there is only one material, return that directly instead of array
+		var filtered = materials.filter( Boolean );
+		if ( filtered.length === 1 ) return filtered[ 0 ];
 
-			var node = {
-				name: name
-			};
-			this.currentForm = node;
-			this.currentNode = node;
+		return materials;
 
-			this.currentFormEnd = this.reader.offset + length;
+	},
 
+	getMaterialByName( name ) {
 
-		},
+		return this.materials.filter( function ( m ) {
 
-		// collect attributes from all nodes at the top level of a surface
-		parseConnections( length ) {
+			return m.name === name;
 
-			this.currentFormEnd = this.reader.offset + length;
-			this.parentForm = this.currentForm;
+		} )[ 0 ];
 
-			this.currentForm = this.currentSurface.connections;
+	},
 
-		},
+	// If the material has an aoMap, duplicate UVs
+	duplicateUVs( geometry, materials ) {
 
-		// surface node attribute data, e.g. specular, roughness etc
-		parseEntryForm( length ) {
+		var duplicateUVs = false;
 
-			this.reader.skip( 8 ); // NAME + length
-			var name = this.reader.getString();
-			this.currentForm = this.currentNode.attributes;
+		if ( ! Array.isArray( materials ) ) {
 
-			this.setupForm( name, length );
+			if ( materials.aoMap ) duplicateUVs = true;
 
-		},
+		} else {
+
+			materials.forEach( function ( material ) {
+
+				if ( material.aoMap ) duplicateUVs = true;
+
+			} );
+
+		}
 
-		// parse values from material - doesn't match up to other LWO3 data types
-		// sub form of entry form
-		parseValueForm() {
+		if ( ! duplicateUVs ) return;
 
-			this.reader.skip( 8 ); // unknown + length
+		geometry.addAttribute( 'uv2', new THREE.BufferAttribute( geometry.attributes.uv.array, 2 ) );
 
-			var valueType = this.reader.getString();
+	},
 
-			if ( valueType === 'double' ) {
+};
 
-				this.currentForm.value = this.reader.getUint64();
+function MaterialParser( textureLoader ) {
 
-			} else if ( valueType === 'int' ) {
+	this.textureLoader = textureLoader;
 
-				this.currentForm.value = this.reader.getUint32();
+}
 
-			} else if ( valueType === 'vparam' ) {
+MaterialParser.prototype = {
 
-				this.reader.skip( 24 );
-				this.currentForm.value = this.reader.getFloat64();
+	constructor: MaterialParser,
 
-			} else if ( valueType === 'vparam3' ) {
+	parse: function () {
 
-				this.reader.skip( 24 );
-				this.currentForm.value = this.reader.getFloat64Array( 3 );
+		var materials = [];
+		this.textures = {};
 
+		for ( var name in lwoTree.materials ) {
+
+			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 ) );
 
 			}
 
-		},
+		}
 
-		// holds various data about texture node image state
-		// Data other thanmipMapLevel unknown
-		parseImageStateForm() {
+		return materials;
 
-			this.reader.skip( 8 ); // unknown
+	},
 
-			this.currentForm.mipMapLevel = this.reader.getFloat32();
+	parseMaterial( materialData, name, textures ) {
 
-		},
+		var params = {
+			name: name,
+			side: this.getSide( materialData.attributes ),
+			flatShading: this.getSmooth( materialData.attributes ),
+		};
 
-		// LWO2 style image data node OR LWO3 textures defined at top level in editor (not as SURF node)
-		parseImageMap( length ) {
+		var connections = this.parseConnections( materialData.connections, materialData.nodes );
 
-			this.currentFormEnd = this.reader.offset + length;
-			this.parentForm = this.currentForm;
+		var maps = this.parseTextureNodes( connections.maps );
 
-			if ( ! this.currentForm.maps ) this.currentForm.maps = [];
+		this.parseAttributeImageMaps( connections.attributes, textures, maps, materialData.maps );
 
-			var map = {};
-			this.currentForm.maps.push( map );
-			this.currentForm = map;
+		var attributes = this.parseAttributes( connections.attributes, maps );
 
-			this.reader.skip( 10 ); // unknown, could be an issue if it contains a VX
+		this.parseEnvMap( connections, maps, attributes );
 
-		},
+		params = Object.assign( maps, params );
+		params = Object.assign( params, attributes );
 
-		parseTextureNodeAttribute( type ) {
+		var materialType = this.getMaterialType( connections.attributes );
 
-			this.reader.skip( 28 ); // FORM + length + VPRM + unknown + Uint32 x2 + float32
+		return new materialType( params );
 
-			this.reader.skip( 20 ); // FORM + length + VPVL + float32 + Uint32
+	},
 
-			switch ( type ) {
+	parseMaterialLwo2( materialData, name/*, textures*/ ) {
 
-				case 'ISCL':
-					this.currentNode.scale = this.reader.getFloat32Array( 3 );
-					break;
-				case 'IPOS':
-					this.currentNode.position = this.reader.getFloat32Array( 3 );
-					break;
-				case 'IROT':
-					this.currentNode.rotation = this.reader.getFloat32Array( 3 );
-					break;
-				case 'IFAL':
-					this.currentNode.falloff = this.reader.getFloat32Array( 3 );
-					break;
+		var params = {
+			name: name,
+			side: this.getSide( materialData.attributes ),
+			flatShading: this.getSmooth( materialData.attributes ),
+		};
 
-				case 'IBMP':
-					this.currentNode.amplitude = this.reader.getFloat32();
-					break;
-				case 'IUTD':
-					this.currentNode.uTiles = this.reader.getFloat32();
-					break;
-				case 'IVTD':
-					this.currentNode.vTiles = this.reader.getFloat32();
-					break;
+		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!
+	getSide( attributes ) {
 
-			this.reader.skip( 2 ); // unknown
+		if ( ! attributes.side ) return THREE.BackSide;
 
+		switch ( attributes.side ) {
 
-		},
+			case 0:
+			case 1:
+				return THREE.BackSide;
+			case 2: return THREE.FrontSide;
+			case 3: return THREE.DoubleSide;
 
-		// ENVL forms are currently ignored
-		parseEnvelope( length ) {
+		}
 
-			this.reader.skip( length - 4 ); // skipping  entirely for now
+	},
 
-		},
+	getSmooth( attributes ) {
 
-		///
-		// CHUNK PARSING METHODS
-		///
+		if ( ! attributes.smooth ) return true;
+		return ! attributes.smooth;
 
-		// clips can either be defined inside a surface node, or at the top
-		// level and they have a different format in each case
-		parseClip( length ) {
+	},
 
-			var tag = this.reader.getIDTag();
+	parseConnections( connections, nodes ) {
 
-			// inside surface node
-			if ( tag === 'FORM' ) {
+		var materialConnections = {
+			maps: {}
+		};
 
-				this.reader.skip( 16 );
+		var inputName = connections.inputName;
+		var inputNodeName = connections.inputNodeName;
+		var nodeName = connections.nodeName;
 
-				this.currentNode.fileName = this.reader.getString();
+		var self = this;
+		inputName.forEach( function ( name, index ) {
 
-				return;
+			if ( name === 'Material' ) {
+
+				var matNode = self.getNodeByRefName( inputNodeName[ index ], nodes );
+				materialConnections.attributes = matNode.attributes;
+				materialConnections.envMap = matNode.fileName;
+				materialConnections.name = inputNodeName[ index ];
 
 			}
 
-			// otherwise top level
-			this.reader.setOffset( this.reader.offset - 4 );
+		} );
 
-			this.currentFormEnd = this.reader.offset + length;
-			this.parentForm = this.currentForm;
+		nodeName.forEach( function ( name, index ) {
 
-			this.reader.skip( 8 ); // unknown
+			if ( name === materialConnections.name ) {
 
-			var texture = {
-				index: this.reader.getUint32()
-			};
-			this.tree.textures.push( texture );
-			this.currentForm = texture;
+				materialConnections.maps[ inputName[ index ] ] = self.getNodeByRefName( inputNodeName[ index ], nodes );
 
-		},
+			}
 
-		parseClipLwo2( length ) {
+		} );
 
-			var texture = {
-				index: this.reader.getUint32(),
-				fileName: ""
-			};
+		return materialConnections;
 
-			var readed = 4;
-			// seach STIL block
-			while ( true ) {
+	},
 
-				var tag = this.reader.getIDTag();
-				var n_length = this.reader.getUint16();
-				if ( tag === 'STIL' ) {
+	getNodeByRefName( refName, nodes ) {
 
-					texture.fileName = this.reader.getString();
-					break;
+		for ( var name in nodes ) {
 
-				}
-				readed += 4 + n_length;
-				if ( n_length >= length ) {
+			if ( nodes[ name ].refName === refName ) return nodes[ name ];
 
-					break;
+		}
 
-				}
+	},
+
+	parseTextureNodes( textureNodes ) {
+
+		var maps = {};
+
+		for ( var name in textureNodes ) {
+
+			var node = textureNodes[ name ];
+			var path = node.fileName;
+
+			if ( ! path ) return;
+
+			var texture = this.loadTexture( path );
+
+			if ( node.widthWrappingMode !== undefined ) texture.wrapS = this.getWrappingType( node.widthWrappingMode );
+			if ( node.heightWrappingMode !== undefined ) texture.wrapT = this.getWrappingType( node.heightWrappingMode );
+
+			switch ( name ) {
+
+				case 'Color':
+					maps.map = texture;
+					break;
+				case 'Roughness':
+					maps.roughnessMap = texture;
+					maps.roughness = 0.5;
+					break;
+				case 'Specular':
+					maps.specularMap = texture;
+					maps.specular = 0xffffff;
+					break;
+				case 'Luminous':
+					maps.emissiveMap = texture;
+					maps.emissive = 0x808080;
+					break;
+				case 'Luminous Color':
+					maps.emissive = 0x808080;
+					break;
+				case 'Metallic':
+					maps.metalnessMap = texture;
+					maps.metalness = 0.5;
+					break;
+				case 'Transparency':
+				case 'Alpha':
+					maps.alphaMap = texture;
+					maps.transparent = true;
+					break;
+				case 'Normal':
+					maps.normalMap = texture;
+					if ( node.amplitude !== undefined ) maps.normalScale = new THREE.Vector2( node.amplitude, node.amplitude );
+					break;
+				case 'Bump':
+					maps.bumpMap = texture;
+					break;
 
 			}
 
-			this.tree.textures.push( texture );
-			this.currentForm = texture;
+		}
 
-		},
+		// LWO BSDF materials can have both spec and rough, but this is not valid in three
+		if ( maps.roughnessMap && maps.specularMap ) delete maps.specularMap;
 
-		parseImage() {
+		return maps;
 
-			this.reader.skip( 8 ); // unknown
-			this.currentForm.fileName = this.reader.getString();
+	},
 
-		},
+	// maps can also be defined on individual material attributes, parse those here
+	// This occurs on Standard (Phong) surfaces
+	parseAttributeImageMaps( attributes, textures, maps ) {
 
-		parseXVAL( type, length ) {
+		for ( var name in attributes ) {
 
-			var endOffset = this.reader.offset + length - 4;
-			this.reader.skip( 8 );
+			var attribute = attributes[ name ];
 
-			this.currentForm[ type ] = this.reader.getFloat32();
+			if ( attribute.maps ) {
 
-			this.reader.setOffset( endOffset ); // set end offset directly to skip optional envelope
+				var mapData = attribute.maps[ 0 ];
 
-		},
+				var path = this.getTexturePathByIndex( mapData.imageIndex, textures );
+				if ( ! path ) return;
 
-		parseXVAL3( type, length ) {
+				var texture = this.loadTexture( path );
 
-			var endOffset = this.reader.offset + length - 4;
-			this.reader.skip( 8 );
+				if ( mapData.wrap !== undefined ) texture.wrapS = this.getWrappingType( mapData.wrap.w );
+				if ( mapData.wrap !== undefined ) texture.wrapT = this.getWrappingType( mapData.wrap.h );
 
-			this.currentForm[ type ] = {
-				x: this.reader.getFloat32(),
-				y: this.reader.getFloat32(),
-				z: this.reader.getFloat32(),
-			};
+				switch ( name ) {
 
-			this.reader.setOffset( endOffset );
+					case 'Color':
+						maps.map = texture;
+						break;
+					case 'Diffuse':
+						maps.aoMap = texture;
+						break;
+					case 'Roughness':
+						maps.roughnessMap = texture;
+						maps.roughness = 1;
+						break;
+					case 'Specular':
+						maps.specularMap = texture;
+						maps.specular = 0xffffff;
+						break;
+					case 'Luminosity':
+						maps.emissiveMap = texture;
+						maps.emissive = 0x808080;
+						break;
+					case 'Metallic':
+						maps.metalnessMap = texture;
+						maps.metalness = 1;
+						break;
+					case 'Transparency':
+					case 'Alpha':
+						maps.alphaMap = texture;
+						maps.transparent = true;
+						break;
+					case 'Normal':
+						maps.normalMap = texture;
+						break;
+					case 'Bump':
+						maps.bumpMap = texture;
+						break;
 
-		},
+				}
 
-		// Tags associated with an object
-		// OTAG { type[ID4], tag-string[S0] }
-		parseObjectTag() {
+			}
 
-			if ( ! this.tree.objectTags ) this.tree.objectTags = {};
+		}
 
-			this.tree.objectTags[ this.reader.getIDTag() ] = {
-				tagString: this.reader.getString()
-			};
+	},
 
-		},
+	parseAttributes( attributes, maps ) {
 
-		// Signals the start of a new layer. All the data chunks which follow will be included in this layer until another layer chunk is encountered.
-		// LAYR: number[U2], flags[U2], pivot[VEC12], name[S0], parent[U2]
-		parseLayer( length ) {
+		var params = {};
 
-			var layer = {
-				number: this.reader.getUint16(),
-				flags: this.reader.getUint16(), // If the least significant bit of flags is set, the layer is hidden.
-				pivot: this.reader.getFloat32Array( 3 ), // Note: this seems to be superflous, as the geometry is translated when pivot is present
-				name: this.reader.getString(),
-			};
+		// don't use color data if color map is present
+		if ( attributes.Color && ! maps.map ) {
 
-			this.tree.layers.push( layer );
-			this.currentLayer = layer;
+			params.color = new THREE.Color().fromArray( attributes.Color.value );
 
-			var parsedLength = 16 + stringOffset( this.currentLayer.name ); // index ( 2 ) + flags( 2 ) + pivot( 12 ) + stringlength
+		} else params.color = new THREE.Color();
 
-			// if we have not reached then end of the layer block, there must be a parent defined
-			this.currentLayer.parent = ( parsedLength < length ) ? this.reader.getUint16() : - 1; // omitted or -1 for no parent
 
-		},
+		if ( attributes.Transparency && attributes.Transparency.value !== 0 ) {
 
-		// VEC12 * ( F4 + F4 + F4 ) array of x,y,z vectors
-		// Converting from left to right handed coordinate system:
-		// x -> -x and switch material FrontSide -> BackSide
-		parsePoints( length ) {
+			params.opacity = 1 - attributes.Transparency.value;
+			params.transparent = true;
 
-			this.currentPoints = [];
-			for ( var i = 0; i < length / 4; i += 3 ) {
+		}
 
-				// z -> -z to match three.js right handed coords
-				this.currentPoints.push( this.reader.getFloat32(), this.reader.getFloat32(), - this.reader.getFloat32() );
+		if ( attributes[ 'Bump Height' ] ) params.bumpScale = attributes[ 'Bump Height' ].value * 0.1;
 
-			}
+		if ( attributes[ 'Refraction Index' ] ) params.refractionRatio = 1 / attributes[ 'Refraction Index' ].value;
+
+		this.parsePhysicalAttributes( params, attributes, maps );
+		this.parseStandardAttributes( params, attributes, maps );
+		this.parsePhongAttributes( params, attributes, maps );
 
-		},
+		return params;
 
-		// parse VMAP or VMAD
-		// Associates a set of floating-point vectors with a set of points.
-		// VMAP: { type[ID4], dimension[U2], name[S0], ( vert[VX], value[F4] # dimension ) * }
+	},
 
-		// VMAD Associates a set of floating-point vectors with the vertices of specific polygons.
-		// Similar to VMAP UVs, but associates with polygon vertices rather than points
-		// to solve to problem of UV seams:  VMAD chunks are paired with VMAPs of the same name,
-		// if they exist. The vector values in the VMAD will then replace those in the
-		// corresponding VMAP, but only for calculations involving the specified polygons.
-		// VMAD { type[ID4], dimension[U2], name[S0], ( vert[VX], poly[VX], value[F4] # dimension ) * }
-		parseVertexMapping( length, discontinuous ) {
+	parsePhysicalAttributes( params, attributes/*, maps*/ ) {
 
-			var finalOffset = this.reader.offset + length;
+		if ( attributes.Clearcoat && attributes.Clearcoat.value > 0 ) {
 
-			var channelName = this.reader.getString();
+			params.clearCoat = attributes.Clearcoat.value;
 
-			if ( this.reader.offset === finalOffset ) {
+			if ( attributes[ 'Clearcoat Gloss' ] ) {
 
-				// then we are in a texture node and the VMAP chunk is just a reference to a UV channel name
-				this.currentForm.UVChannel = channelName;
-				return;
+				params.clearCoatRoughness = 0.5 * ( 1 - attributes[ 'Clearcoat Gloss' ].value );
 
 			}
 
-			// otherwise reset to initial length and parse normal VMAP CHUNK
-			this.reader.setOffset( this.reader.offset - stringOffset( channelName ) );
+		}
 
-			var type = this.reader.getIDTag();
+	},
 
-			this.reader.getUint16(); // dimension
-			var name = this.reader.getString();
+	parseStandardAttributes( params, attributes, maps ) {
 
-			var remainingLength = length - 6 - stringOffset( name );
 
-			switch ( type ) {
+		if ( attributes.Luminous ) {
 
-				case 'TXUV':
-					this.parseUVMapping( name, finalOffset, discontinuous );
-					break;
-				case 'MORF':
-				case 'SPOT':
-					this.parseMorphTargets( name, finalOffset, type ); // can't be discontinuous
-					break;
-				// unsupported VMAPs
-				case 'APSL':
-				case 'NORM':
-				case 'WGHT':
-				case 'MNVW':
-				case 'PICK':
-				case 'RGB ':
-				case 'RGBA':
-					this.reader.skip( remainingLength );
-					break;
-				default:
-					console.warn( 'LWOLoader: unknown vertex map type: ' + type );
-					this.reader.skip( remainingLength );
+			params.emissiveIntensity = attributes.Luminous.value;
+
+			if ( attributes[ 'Luminous Color' ] && ! maps.emissive ) {
+
+				params.emissive = new THREE.Color().fromArray( attributes[ 'Luminous Color' ].value );
+
+			} else {
+
+				params.emissive = new THREE.Color( 0x808080 );
 
 			}
 
-		},
+		}
 
-		parseUVMapping( name, finalOffset, discontinuous ) {
+		if ( attributes.Roughness && ! maps.roughnessMap ) params.roughness = attributes.Roughness.value;
+		if ( attributes.Metallic && ! maps.metalnessMap ) params.metalness = attributes.Metallic.value;
 
-			var uvIndices = [];
-			var polyIndices = [];
-			var uvs = [];
+	},
 
-			while ( this.reader.offset < finalOffset ) {
+	parsePhongAttributes( params, attributes, maps ) {
 
-				uvIndices.push( this.reader.getVariableLengthIndex() );
+		if ( attributes.Diffuse ) params.color.multiplyScalar( attributes.Diffuse.value );
 
-				if ( discontinuous ) polyIndices.push( this.reader.getVariableLengthIndex() );
+		if ( attributes.Reflection ) {
 
-				uvs.push( this.reader.getFloat32(), this.reader.getFloat32() );
+			params.reflectivity = attributes.Reflection.value;
+			params.combine = THREE.AddOperation;
 
-			}
+		}
 
-			if ( discontinuous ) {
+		if ( attributes.Luminosity ) {
 
-				if ( ! this.currentLayer.discontinuousUVs ) this.currentLayer.discontinuousUVs = {};
+			params.emissiveIntensity = attributes.Luminosity.value;
 
-				this.currentLayer.discontinuousUVs[ name ] = {
-					uvIndices: uvIndices,
-					polyIndices: polyIndices,
-					uvs: uvs,
-				};
+			if ( ! maps.emissiveMap && ! maps.map ) {
 
-			} else {
+				params.emissive = params.color;
 
-				if ( ! this.currentLayer.uvs ) this.currentLayer.uvs = {};
+			} else {
 
-				this.currentLayer.uvs[ name ] = {
-					uvIndices: uvIndices,
-					uvs: uvs,
-				};
+				params.emissive = new THREE.Color( 0x808080 );
 
 			}
 
-		},
+		}
 
-		parseMorphTargets( name, finalOffset, type ) {
+		// parse specular if there is no roughness - we will interpret the material as 'Phong' in this case
+		if ( ! attributes.Roughness && attributes.Specular && ! maps.specularMap ) {
 
-			var indices = [];
-			var points = [];
+			if ( attributes[ 'Color Highlight' ] ) {
 
-			type = ( type === 'MORF' ) ? 'relative' : 'absolute';
+				params.specular = new THREE.Color().setScalar( attributes.Specular.value ).lerp( params.color.clone().multiplyScalar( attributes.Specular.value ), attributes[ 'Color Highlight' ].value );
 
-			while ( this.reader.offset < finalOffset ) {
+			} else {
 
-				indices.push( this.reader.getVariableLengthIndex() );
-				// z -> -z to match three.js right handed coords
-				points.push( this.reader.getFloat32(), this.reader.getFloat32(), - this.reader.getFloat32() );
+				params.specular = new THREE.Color().setScalar( attributes.Specular.value );
 
 			}
 
-			if ( ! this.currentLayer.morphTargets ) this.currentLayer.morphTargets = {};
+		}
 
-			this.currentLayer.morphTargets[ name ] = {
-				indices: indices,
-				points: points,
-				type: type,
-			};
+		if ( params.specular && attributes.Glossiness ) params.shininess = 7 + Math.pow( 2, attributes.Glossiness.value * 12 + 2 );
 
-		},
+	},
 
-		// A list of polygons for the current layer.
-		// POLS { type[ID4], ( numvert+flags[U2], vert[VX] # numvert ) * }
-		parsePolygonList( length ) {
+	parseEnvMap( connections, maps, attributes ) {
 
-			var finalOffset = this.reader.offset + length;
-			var type = this.reader.getIDTag();
+		if ( connections.envMap ) {
 
-			var indices = [];
+			var envMap = this.loadTexture( connections.envMap );
 
-			// hold a list of polygon sizes, to be split up later
-			var polygonDimensions = [];
+			if ( attributes.transparent && attributes.opacity < 0.999 ) {
 
-			while ( this.reader.offset < finalOffset ) {
+				envMap.mapping = THREE.EquirectangularRefractionMapping;
 
-				var numverts = this.reader.getUint16();
+				// Reflectivity and refraction mapping don't work well together in Phong materials
+				if ( attributes.reflectivity !== undefined ) {
 
-				//var flags = numverts & 64512; // 6 high order bits are flags - ignoring for now
-				numverts = numverts & 1023; // remaining ten low order bits are vertex num
-				polygonDimensions.push( numverts );
+					delete attributes.reflectivity;
+					delete attributes.combine;
 
-				for ( var j = 0; j < numverts; j ++ ) indices.push( this.reader.getVariableLengthIndex() );
+				}
 
-			}
+				if ( attributes.metalness !== undefined ) {
 
-			var geometryData = {
-				type: type,
-				vertexIndices: indices,
-				polygonDimensions: polygonDimensions,
-				points: this.currentPoints
-			};
+					delete attributes.metalness;
 
-			// Note: assuming that all polys will be lines or points if the first is
-			if ( polygonDimensions[ 0 ] === 1 ) geometryData.type = 'points';
-			else if ( polygonDimensions[ 0 ] === 2 ) geometryData.type = 'lines';
+				}
 
-			this.currentLayer.geometry = geometryData;
+			} else envMap.mapping = THREE.EquirectangularReflectionMapping;
 
-		},
+			maps.envMap = envMap;
 
-		// Lists the tag strings that can be associated with polygons by the PTAG chunk.
-		// TAGS { tag-string[S0] * }
-		parseTagStrings( length ) {
+		}
 
-			this.tree.tags = this.reader.getStringArray( length );
+	},
 
-		},
+	// get texture defined at top level by its index
+	getTexturePathByIndex( index ) {
 
-		// Associates tags of a given type with polygons in the most recent POLS chunk.
-		// PTAG { type[ID4], ( poly[VX], tag[U2] ) * }
-		parsePolygonTagMapping( length ) {
+		var fileName = '';
 
-			var finalOffset = this.reader.offset + length;
-			var type = this.reader.getIDTag();
-			if ( type === 'SURF' ) this.parseMaterialIndices( finalOffset );
-			else { //PART, SMGP, COLR not supported
+		if ( ! lwoTree.textures ) return fileName;
 
-				this.reader.skip( length - 4 );
+		lwoTree.textures.forEach( function ( texture ) {
 
-			}
+			if ( texture.index === index ) fileName = texture.fileName;
 
-		},
+		} );
 
-		parseMaterialIndices( finalOffset ) {
+		return fileName;
 
-			// array holds polygon index followed by material index
-			this.currentLayer.geometry.materialIndices = [];
+	},
 
-			var initialMatIndex;
+	loadTexture( path ) {
 
-			while ( this.reader.offset < finalOffset ) {
+		if ( ! path ) return null;
 
-				var polygonIndex = this.reader.getVariableLengthIndex();
-				var materialIndex = this.reader.getUint16();
+		var texture;
 
-				if ( ! initialMatIndex ) initialMatIndex = materialIndex; // set up first mat index
+		texture = this.textureLoader.load(
+			path,
+			undefined,
+			undefined,
+			function () {
 
-				this.currentLayer.geometry.materialIndices.push( polygonIndex, materialIndex );
+				console.warn( 'LWOLoader: non-standard resource hierarchy. Use \`resourcePath\` parameter to specify root content directory.' );
 
 			}
+		);
 
-		},
-
-		parseUnknownCHUNK( blockID, length ) {
+		return texture;
 
-			console.warn( 'LWOLoader: unknown chunk type: ' + blockID + ' length: ' + length );
+	},
 
-			// print the chunk plus some bytes padding either side
-			// printBuffer( this.reader.dv.buffer, this.reader.offset - 20, length + 40 );
+	// 0 = Reset, 1 = Repeat, 2 = Mirror, 3 = Edge
+	getWrappingType( num ) {
 
-			var data = this.reader.getString( length );
+		switch ( num ) {
 
-			this.currentForm[ blockID ] = data;
+			case 0:
+				console.warn( 'LWOLoader: "Reset" texture wrapping type is not supported in three.js' );
+				return THREE.ClampToEdgeWrapping;
+			case 1: return THREE.RepeatWrapping;
+			case 2: return THREE.MirroredRepeatWrapping;
+			case 3: return THREE.ClampToEdgeWrapping;
 
 		}
 
-	};
-
-	function DataViewReader( buffer ) {
+	},
 
-		// For testing: dump whole buffer to console as a string
-		// printBuffer( buffer, 0, buffer.byteLength );
+	getMaterialType( nodeData ) {
 
-		this.dv = new DataView( buffer );
-		this.offset = 0;
+		if ( nodeData.Clearcoat && nodeData.Clearcoat.value > 0 ) return THREE.MeshPhysicalMaterial;
+		if ( nodeData.Roughness ) return THREE.MeshStandardMaterial;
+		return THREE.MeshPhongMaterial;
 
 	}
 
-	DataViewReader.prototype = {
+};
 
-		constructor: DataViewReader,
+function GeometryParser() {}
 
-		size: function () {
+GeometryParser.prototype = {
 
-			return this.dv.buffer.byteLength;
+	constructor: GeometryParser,
 
-		},
+	parse( geoData, layer ) {
 
-		setOffset( offset ) {
+		var geometry = new THREE.BufferGeometry();
 
-			if ( offset > 0 && offset < this.dv.buffer.byteLength ) {
+		geometry.addAttribute( 'position', new THREE.Float32BufferAttribute( geoData.points, 3 ) );
 
-				this.offset = offset;
+		var indices = this.splitIndices( geoData.vertexIndices, geoData.polygonDimensions );
+		geometry.setIndex( indices );
 
-			} else {
+		this.parseGroups( geometry, geoData );
 
-				console.error( 'LWOLoader: invalid buffer offset' );
+		geometry.computeVertexNormals();
 
-			}
+		this.parseUVs( geometry, layer, indices );
+		this.parseMorphTargets( geometry, layer, indices );
 
-		},
+		// TODO: z may need to be reversed to account for coordinate system change
+		geometry.translate( - layer.pivot[ 0 ], - layer.pivot[ 1 ], - layer.pivot[ 2 ] );
 
-		endOfFile: function () {
+		// var userData = geometry.userData;
+		// geometry = geometry.toNonIndexed()
+		// geometry.userData = userData;
 
-			if ( this.offset >= this.size() ) return true;
-			return false;
+		return geometry;
 
-		},
+	},
 
-		skip: function ( length ) {
+	// split quads into tris
+	splitIndices( indices, polygonDimensions ) {
 
-			this.offset += length;
+		var remappedIndices = [];
 
-		},
+		var i = 0;
+		polygonDimensions.forEach( function ( dim ) {
 
-		getUint8: function () {
+			if ( dim < 4 ) {
 
-			var value = this.dv.getUint8( this.offset );
-			this.offset += 1;
-			return value;
+				for ( var k = 0; k < dim; k ++ ) remappedIndices.push( indices[ i + k ] );
 
-		},
+			} else if ( dim === 4 ) {
 
-		getUint16: function () {
+				remappedIndices.push(
+					indices[ i ],
+					indices[ i + 1 ],
+					indices[ i + 2 ],
 
-			var value = this.dv.getUint16( this.offset );
-			this.offset += 2;
-			return value;
+					indices[ i ],
+					indices[ i + 2 ],
+					indices[ i + 3 ]
 
-		},
+				);
 
-		getInt32: function () {
+			} else if ( dim > 4 ) {
 
-			var value = this.dv.getInt32( this.offset, false );
-			this.offset += 4;
-			return value;
+				for ( var k = 1; k < dim - 1; k ++ ) {
 
-		},
+					remappedIndices.push( indices[ i ], indices[ i + k ], indices[ i + k + 1 ] );
 
-		getUint32: function () {
+				}
 
-			var value = this.dv.getUint32( this.offset, false );
-			this.offset += 4;
-			return value;
+				console.warn( 'LWOLoader: polygons with greater than 4 sides are not supported' );
 
-		},
+			}
 
-		getUint64: function () {
+			i += dim;
 
-			var low, high;
+		} );
 
-			high = this.getUint32();
-			low = this.getUint32();
-			return high * 0x100000000 + low;
+		return remappedIndices;
 
-		},
+	},
 
-		getFloat32: function () {
+	// NOTE: currently ignoring poly indices and assuming that they are intelligently ordered
+	parseGroups( geometry, geoData ) {
 
-			var value = this.dv.getFloat32( this.offset, false );
-			this.offset += 4;
-			return value;
+		var tags = lwoTree.tags;
+		var matNames = [];
 
-		},
+		var elemSize = 3;
+		if ( geoData.type === 'lines' ) elemSize = 2;
+		if ( geoData.type === 'points' ) elemSize = 1;
 
-		getFloat32Array: function ( size ) {
+		var remappedIndices = this.splitMaterialIndices( geoData.polygonDimensions, geoData.materialIndices );
 
-			var a = [];
+		var indexNum = 0; // create new indices in numerical order
+		var indexPairs = {}; // original indices mapped to numerical indices
 
-			for ( var i = 0; i < size; i ++ ) {
+		var prevMaterialIndex;
 
-				a.push( this.getFloat32() );
+		var prevStart = 0;
+		var currentCount = 0;
 
-			}
+		for ( var i = 0; i < remappedIndices.length; i += 2 ) {
 
-			return a;
+			var materialIndex = remappedIndices[ i + 1 ];
 
-		},
+			if ( i === 0 ) matNames[ indexNum ] = tags[ materialIndex ];
 
-		getFloat64: function () {
+			if ( prevMaterialIndex === undefined ) prevMaterialIndex = materialIndex;
 
-			var value = this.dv.getFloat64( this.offset, this.littleEndian );
-			this.offset += 8;
-			return value;
+			if ( materialIndex !== prevMaterialIndex ) {
 
-		},
+				var currentIndex;
+				if ( indexPairs[ tags[ prevMaterialIndex ] ] ) {
 
-		getFloat64Array: function ( size ) {
+					currentIndex = indexPairs[ tags[ prevMaterialIndex ] ];
 
-			var a = [];
+				} else {
 
-			for ( var i = 0; i < size; i ++ ) {
+					currentIndex = indexNum;
+					indexPairs[ tags[ prevMaterialIndex ] ] = indexNum;
+					matNames[ indexNum ] = tags[ prevMaterialIndex ];
+					indexNum ++;
 
-				a.push( this.getFloat64() );
+				}
+
+				geometry.addGroup( prevStart, currentCount, currentIndex );
+
+				prevStart += currentCount;
+
+				prevMaterialIndex = materialIndex;
+				currentCount = 0;
 
 			}
 
-			return a;
+			currentCount += elemSize;
+
+		}
 
-		},
+		// the loop above doesn't add the last group, do that here.
+		if ( geometry.groups.length > 0 ) {
 
-		// get variable-length index data type
-		// VX ::= index[U2] | (index + 0xFF000000)[U4]
-		// If the index value is less than 65,280 (0xFF00),then VX === U2
-		// otherwise VX === U4 with bits 24-31 set
-		// When reading an index, if the first byte encountered is 255 (0xFF), then
-		// the four-byte form is being used and the first byte should be discarded or masked out.
-		getVariableLengthIndex() {
+			var currentIndex;
+			if ( indexPairs[ tags[ materialIndex ] ] ) {
 
-			var firstByte = this.getUint8();
+				currentIndex = indexPairs[ tags[ materialIndex ] ];
 
-			if ( firstByte === 255 ) {
+			} else {
 
-				return this.getUint8() * 65536 + this.getUint8() * 256 + this.getUint8();
+				currentIndex = indexNum;
+				indexPairs[ tags[ materialIndex ] ] = indexNum;
+				matNames[ indexNum ] = tags[ materialIndex ];
 
 			}
 
-			return firstByte * 256 + this.getUint8();
+			geometry.addGroup( prevStart, currentCount, currentIndex );
+
+		}
+
+		// Mat names from TAGS chunk, used to build up an array of materials for this geometry
+		geometry.userData.matNames = matNames;
+
+	},
 
-		},
+	splitMaterialIndices( polygonDimensions, indices ) {
 
-		// An ID tag is a sequence of 4 bytes containing 7-bit ASCII values
-		getIDTag() {
+		var remappedIndices = [];
 
-			return this.getString( 4 );
+		polygonDimensions.forEach( function ( dim, i ) {
 
-		},
+			if ( dim <= 3 ) {
 
-		getString: function ( size ) {
+				remappedIndices.push( indices[ i * 2 ], indices[ i * 2 + 1 ] );
 
-			if ( size === 0 ) return;
+			} else if ( dim === 4 ) {
 
-			// note: safari 9 doesn't support Uint8Array.indexOf; create intermediate array instead
-			var a = [];
+				remappedIndices.push( indices[ i * 2 ], indices[ i * 2 + 1 ], indices[ i * 2 ], indices[ i * 2 + 1 ] );
 
-			if ( size ) {
+			} else {
 
-				for ( var i = 0; i < size; i ++ ) {
+				 // ignore > 4 for now
+				for ( var k = 0; k < dim - 2; k ++ ) {
 
-					a[ i ] = this.getUint8();
+					remappedIndices.push( indices[ i * 2 ], indices[ i * 2 + 1 ] );
 
 				}
 
-			} else {
+			}
 
-				var currentChar;
-				var len = 0;
+		} );
 
-				while ( currentChar !== 0 ) {
+		return remappedIndices;
 
-					currentChar = this.getUint8();
-					if ( currentChar !== 0 ) a.push( currentChar );
-					len ++;
+	},
 
-				}
+	// UV maps:
+	// 1: are defined via index into an array of points, not into a geometry
+	// - the geometry is also defined by an index into this array, but the indexes may not match
+	// 2: there can be any number of UV maps for a single geometry. Here these are combined,
+	// 	with preference given to the first map encountered
+	// 3: UV maps can be partial - that is, defined for only a part of the geometry
+	// 4: UV maps can be VMAP or VMAD (discontinuous, to allow for seams). In practice, most
+	// UV maps are defined as partially VMAP and partially VMAD
+	// VMADs are currently not supported
+	parseUVs( geometry, layer ) {
 
-				if ( ! isEven( len + 1 ) ) this.getUint8(); // if string with terminating nullbyte is uneven, extra nullbyte is added
+		// start by creating a UV map set to zero for the whole geometry
+		var remappedUVs = Array.from( Array( geometry.attributes.position.count * 2 ), function () {
 
-			}
+			return 0;
 
-			return THREE.LoaderUtils.decodeText( new Uint8Array( a ) );
+		} );
 
-		},
+		for ( var name in layer.uvs ) {
 
-		getStringArray: function ( size ) {
+			var uvs = layer.uvs[ name ].uvs;
+			var uvIndices = layer.uvs[ name ].uvIndices;
 
-			var a = this.getString( size );
-			a = a.split( '\0' );
+			uvIndices.forEach( function ( i, j ) {
 
-			return a.filter( Boolean ); // return array with any empty strings removed
+				remappedUVs[ i * 2 ] = uvs[ j * 2 ];
+				remappedUVs[ i * 2 + 1 ] = uvs[ j * 2 + 1 ];
+
+			} );
 
 		}
 
-	};
+		geometry.addAttribute( 'uv', new THREE.Float32BufferAttribute( remappedUVs, 2 ) );
 
-	// ************** UTILITY FUNCTIONS **************
+	},
 
-	function isEven( num ) {
+	parseMorphTargets( geometry, layer ) {
 
-		return num % 2;
+		var num = 0;
+		for ( var name in layer.morphTargets ) {
 
-	}
+			var remappedPoints = geometry.attributes.position.array.slice();
 
-	// calculate the length of the string in the buffer
-	// this will be string.length + nullbyte + optional padbyte to make the length even
-	function stringOffset( string ) {
+			if ( ! geometry.morphAttributes.position ) geometry.morphAttributes.position = [];
 
-		return string.length + 1 + ( isEven( string.length + 1 ) ? 1 : 0 );
+			var morphPoints = layer.morphTargets[ name ].points;
+			var morphIndices = layer.morphTargets[ name ].indices;
+			var type = layer.morphTargets[ name ].type;
 
-	}
+			morphIndices.forEach( function ( i, j ) {
 
-	// for testing purposes, dump buffer to console
-	// printBuffer( this.reader.dv.buffer, this.reader.offset, length );
-	function printBuffer( buffer, from, to ) {
+				if ( type === 'relative' ) {
 
-		console.log( THREE.LoaderUtils.decodeText( new Uint8Array( buffer, from, to ) ) );
+					remappedPoints[ i * 3 ] += morphPoints[ j * 3 ];
+					remappedPoints[ i * 3 + 1 ] += morphPoints[ j * 3 + 1 ];
+					remappedPoints[ i * 3 + 2 ] += morphPoints[ j * 3 + 2 ];
 
-	}
+				} else {
+
+					remappedPoints[ i * 3 ] = morphPoints[ j * 3 ];
+					remappedPoints[ i * 3 + 1 ] = morphPoints[ j * 3 + 1 ];
+					remappedPoints[ i * 3 + 2 ] = morphPoints[ j * 3 + 2 ];
+
+				}
+
+			} );
+
+			geometry.morphAttributes.position[ num ] = new THREE.Float32BufferAttribute( remappedPoints, 3 );
+			geometry.morphAttributes.position[ num ].name = name;
+
+			num ++;
+
+		}
+
+	},
+
+};
+
+
+// ************** UTILITY FUNCTIONS **************
+
+function extractParentUrl( url, dir ) {
+
+	var index = url.indexOf( dir );
+
+	if ( index === - 1 ) return './';
 
-	return LWOLoader;
+	return url.substr( 0, index );
 
-} )();
+}

+ 8 - 12
examples/js/loaders/PRWMLoader.js

@@ -3,9 +3,7 @@
  * See https://github.com/kchapelier/PRWM for more informations about this file format
  */
 
-( function ( THREE ) {
-
-	'use strict';
+THREE.PRWMLoader = ( function () {
 
 	var bigEndianPlatform = null;
 
@@ -224,13 +222,13 @@
 
 	// Define the public interface
 
-	THREE.PRWMLoader = function PRWMLoader( manager ) {
+	function PRWMLoader( manager ) {
 
 		this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
 
-	};
+	}
 
-	THREE.PRWMLoader.prototype = {
+	PRWMLoader.prototype = {
 
 		constructor: THREE.PRWMLoader,
 
@@ -261,8 +259,6 @@
 
 		parse: function ( arrayBuffer ) {
 
-			console.time( 'PRWMLoader' );
-
 			var data = decodePrwm( arrayBuffer ),
 				attributesKey = Object.keys( data.attributes ),
 				bufferGeometry = new THREE.BufferGeometry(),
@@ -282,18 +278,18 @@
 
 			}
 
-			console.timeEnd( 'PRWMLoader' );
-
 			return bufferGeometry;
 
 		}
 
 	};
 
-	THREE.PRWMLoader.isBigEndianPlatform = function () {
+	PRWMLoader.isBigEndianPlatform = function () {
 
 		return isBigEndianPlatform();
 
 	};
 
-} )( THREE );
+	return PRWMLoader;
+
+} )();

+ 158 - 30
examples/js/loaders/RGBELoader.js

@@ -20,7 +20,7 @@ THREE.RGBELoader.prototype._parser = function ( buffer ) {
 
 	var
 		/* return codes for rgbe routines */
-		RGBE_RETURN_SUCCESS = 0,
+		//RGBE_RETURN_SUCCESS = 0,
 		RGBE_RETURN_FAILURE = - 1,
 
 		/* default error routine.  change this to change error handling */
@@ -47,12 +47,12 @@ THREE.RGBELoader.prototype._parser = function ( buffer ) {
 		},
 
 		/* offsets to red, green, and blue components in a data (float) pixel */
-		RGBE_DATA_RED = 0,
-		RGBE_DATA_GREEN = 1,
-		RGBE_DATA_BLUE = 2,
+		//RGBE_DATA_RED = 0,
+		//RGBE_DATA_GREEN = 1,
+		//RGBE_DATA_BLUE = 2,
 
 		/* number of floats per pixel, use 4 since stored in rgba image format */
-		RGBE_DATA_SIZE = 4,
+		//RGBE_DATA_SIZE = 4,
 
 		/* flags indicating which fields in an rgbe_header_info are valid */
 		RGBE_VALID_PROGRAMTYPE = 1,
@@ -313,8 +313,84 @@ THREE.RGBELoader.prototype._parser = function ( buffer ) {
 
 			return data_rgba;
 
+		};
+
+	var RGBEByteToRGBFloat = function ( sourceArray, sourceOffset, destArray, destOffset ) {
+
+		var e = sourceArray[ sourceOffset + 3 ];
+		var scale = Math.pow( 2.0, e - 128.0 ) / 255.0;
+
+		destArray[ destOffset + 0 ] = sourceArray[ sourceOffset + 0 ] * scale;
+		destArray[ destOffset + 1 ] = sourceArray[ sourceOffset + 1 ] * scale;
+		destArray[ destOffset + 2 ] = sourceArray[ sourceOffset + 2 ] * scale;
+
+	};
+
+	var RGBEByteToRGBHalf = ( function () {
+
+		// Source: http://gamedev.stackexchange.com/questions/17326/conversion-of-a-number-from-single-precision-floating-point-representation-to-a/17410#17410
+
+		var floatView = new Float32Array( 1 );
+		var int32View = new Int32Array( floatView.buffer );
+
+		/* This method is faster than the OpenEXR implementation (very often
+		 * used, eg. in Ogre), with the additional benefit of rounding, inspired
+		 * by James Tursa?s half-precision code. */
+		function toHalf( val ) {
+
+			floatView[ 0 ] = val;
+			var x = int32View[ 0 ];
+
+			var bits = ( x >> 16 ) & 0x8000; /* Get the sign */
+			var m = ( x >> 12 ) & 0x07ff; /* Keep one extra bit for rounding */
+			var e = ( x >> 23 ) & 0xff; /* Using int is faster here */
+
+			/* If zero, or denormal, or exponent underflows too much for a denormal
+			 * half, return signed zero. */
+			if ( e < 103 ) return bits;
+
+			/* If NaN, return NaN. If Inf or exponent overflow, return Inf. */
+			if ( e > 142 ) {
+
+				bits |= 0x7c00;
+				/* If exponent was 0xff and one mantissa bit was set, it means NaN,
+						 * not Inf, so make sure we set one mantissa bit too. */
+				bits |= ( ( e == 255 ) ? 0 : 1 ) && ( x & 0x007fffff );
+				return bits;
+
+			}
+
+			/* If exponent underflows but not too much, return a denormal */
+			if ( e < 113 ) {
+
+				m |= 0x0800;
+				/* Extra rounding may overflow and set mantissa to 0 and exponent
+				 * to 1, which is OK. */
+				bits |= ( m >> ( 114 - e ) ) + ( ( m >> ( 113 - e ) ) & 1 );
+				return bits;
+
+			}
+
+			bits |= ( ( e - 112 ) << 10 ) | ( m >> 1 );
+			/* Extra rounding. An overflow will set mantissa to 0 and increment
+			 * the exponent, which is OK. */
+			bits += m & 1;
+			return bits;
+
 		}
-	;
+
+		return function ( sourceArray, sourceOffset, destArray, destOffset ) {
+
+			var e = sourceArray[ sourceOffset + 3 ];
+			var scale = Math.pow( 2.0, e - 128.0 ) / 255.0;
+
+			destArray[ destOffset + 0 ] = toHalf( sourceArray[ sourceOffset + 0 ] * scale );
+			destArray[ destOffset + 1 ] = toHalf( sourceArray[ sourceOffset + 1 ] * scale );
+			destArray[ destOffset + 2 ] = toHalf( sourceArray[ sourceOffset + 2 ] * scale );
+
+		};
+
+	} )();
 
 	var byteArray = new Uint8Array( buffer );
 	byteArray.pos = 0;
@@ -324,46 +400,55 @@ THREE.RGBELoader.prototype._parser = function ( buffer ) {
 
 		var w = rgbe_header_info.width,
 			h = rgbe_header_info.height,
-			image_rgba_data = RGBE_ReadPixels_RLE( byteArray.subarray( byteArray.pos ), w, h )
-		;
+			image_rgba_data = RGBE_ReadPixels_RLE( byteArray.subarray( byteArray.pos ), w, h );
+
 		if ( RGBE_RETURN_FAILURE !== image_rgba_data ) {
 
-			if ( this.type === THREE.UnsignedByteType ) {
+			switch ( this.type ) {
 
-				var data = image_rgba_data;
-				var format = THREE.RGBEFormat; // handled as THREE.RGBAFormat in shaders
-				var type = THREE.UnsignedByteType;
+				case THREE.UnsignedByteType:
 
-			} else if ( this.type === THREE.FloatType ) {
+					var data = image_rgba_data;
+					var format = THREE.RGBEFormat; // handled as THREE.RGBAFormat in shaders
+					var type = THREE.UnsignedByteType;
+					break;
 
-				var RGBEByteToRGBFloat = function ( sourceArray, sourceOffset, destArray, destOffset ) {
+				case THREE.FloatType:
 
-					var e = sourceArray[ sourceOffset + 3 ];
-					var scale = Math.pow( 2.0, e - 128.0 ) / 255.0;
+					var numElements = ( image_rgba_data.length / 4 ) * 3;
+					var floatArray = new Float32Array( numElements );
 
-					destArray[ destOffset + 0 ] = sourceArray[ sourceOffset + 0 ] * scale;
-					destArray[ destOffset + 1 ] = sourceArray[ sourceOffset + 1 ] * scale;
-					destArray[ destOffset + 2 ] = sourceArray[ sourceOffset + 2 ] * scale;
+					for ( var j = 0; j < numElements; j ++ ) {
 
-				};
+						RGBEByteToRGBFloat( image_rgba_data, j * 4, floatArray, j * 3 );
 
-				var numElements = ( image_rgba_data.length / 4 ) * 3;
-				var floatArray = new Float32Array( numElements );
+					}
 
-				for ( var j = 0; j < numElements; j ++ ) {
+					var data = floatArray;
+					var format = THREE.RGBFormat;
+					var type = THREE.FloatType;
+					break;
 
-					RGBEByteToRGBFloat( image_rgba_data, j * 4, floatArray, j * 3 );
+				case THREE.HalfFloatType:
 
-				}
+					var numElements = ( image_rgba_data.length / 4 ) * 3;
+					var halfArray = new Uint16Array( numElements );
 
-				var data = floatArray;
-				var format = THREE.RGBFormat;
-				var type = THREE.FloatType;
+					for ( var j = 0; j < numElements; j ++ ) {
+
+						RGBEByteToRGBHalf( image_rgba_data, j * 4, halfArray, j * 3 );
+
+					}
 
+					var data = halfArray;
+					var format = THREE.RGBFormat;
+					var type = THREE.HalfFloatType;
+					break;
 
-			} else {
+				default:
 
-				console.error( 'THREE.RGBELoader: unsupported type: ', this.type );
+					console.error( 'THREE.RGBELoader: unsupported type: ', this.type );
+					break;
 
 			}
 
@@ -391,3 +476,46 @@ THREE.RGBELoader.prototype.setType = function ( value ) {
 	return this;
 
 };
+
+THREE.RGBELoader.prototype.load = function ( url, onLoad, onProgress, onError ) {
+
+	function onLoadCallback( texture, texData ) {
+
+		switch ( texture.type ) {
+
+			case THREE.UnsignedByteType:
+
+				texture.encoding = THREE.RGBEEncoding;
+				texture.minFilter = THREE.NearestFilter;
+				texture.magFilter = THREE.NearestFilter;
+				texture.generateMipmaps = false;
+				texture.flipY = true;
+				break;
+
+			case THREE.FloatType:
+
+				texture.encoding = THREE.LinearEncoding;
+				texture.minFilter = THREE.LinearFilter;
+				texture.magFilter = THREE.LinearFilter;
+				texture.generateMipmaps = false;
+				texture.flipY = true;
+				break;
+
+			case THREE.HalfFloatType:
+
+				texture.encoding = THREE.LinearEncoding;
+				texture.minFilter = THREE.LinearFilter;
+				texture.magFilter = THREE.LinearFilter;
+				texture.generateMipmaps = false;
+				texture.flipY = true;
+				break;
+
+		}
+
+		if ( onLoad ) onLoad( texture, texData );
+
+	}
+
+	return THREE.DataTextureLoader.prototype.load.call( this, url, onLoadCallback, onProgress, onError );
+
+};

+ 7 - 7
examples/js/loaders/SVGLoader.js

@@ -56,37 +56,37 @@ THREE.SVGLoader.prototype = {
 
 				case 'path':
 					style = parseStyle( node, style );
-					if ( node.hasAttribute( 'd' ) ) path = parsePathNode( node, style );
+					if ( node.hasAttribute( 'd' ) ) path = parsePathNode( node );
 					break;
 
 				case 'rect':
 					style = parseStyle( node, style );
-					path = parseRectNode( node, style );
+					path = parseRectNode( node );
 					break;
 
 				case 'polygon':
 					style = parseStyle( node, style );
-					path = parsePolygonNode( node, style );
+					path = parsePolygonNode( node );
 					break;
 
 				case 'polyline':
 					style = parseStyle( node, style );
-					path = parsePolylineNode( node, style );
+					path = parsePolylineNode( node );
 					break;
 
 				case 'circle':
 					style = parseStyle( node, style );
-					path = parseCircleNode( node, style );
+					path = parseCircleNode( node );
 					break;
 
 				case 'ellipse':
 					style = parseStyle( node, style );
-					path = parseEllipseNode( node, style );
+					path = parseEllipseNode( node );
 					break;
 
 				case 'line':
 					style = parseStyle( node, style );
-					path = parseLineNode( node, style );
+					path = parseLineNode( node );
 					break;
 
 				default:

+ 188 - 186
examples/js/loaders/TDSLoader.js

@@ -9,8 +9,6 @@
  * @constructor
  */
 
-'use strict';
-
 THREE.TDSLoader = function ( manager ) {
 
 	this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
@@ -63,7 +61,7 @@ THREE.TDSLoader.prototype = {
 	 * @method parse
 	 * @param {ArrayBuffer} arraybuffer Arraybuffer data to be loaded.
 	 * @param {String} path Path for external resources.
-	 * @return {Object3D} Group loaded from 3ds file.
+	 * @return {Group} Group loaded from 3ds file.
 	 */
 	parse: function ( arraybuffer, path ) {
 
@@ -89,6 +87,7 @@ THREE.TDSLoader.prototype = {
 	 *
 	 * @method readFile
 	 * @param {ArrayBuffer} arraybuffer Arraybuffer data to be loaded.
+	 * @param {String} path Path for external resources.
 	 */
 	readFile: function ( arraybuffer, path ) {
 
@@ -132,6 +131,7 @@ THREE.TDSLoader.prototype = {
 	 *
 	 * @method readMeshData
 	 * @param {Dataview} data Dataview in use.
+	 * @param {String} path Path for external resources.
 	 */
 	readMeshData: function ( data, path ) {
 
@@ -216,6 +216,7 @@ THREE.TDSLoader.prototype = {
 	 *
 	 * @method readMaterialEntry
 	 * @param {Dataview} data Dataview in use.
+	 * @param {String} path Path for external resources.
 	 */
 	readMaterialEntry: function ( data, path ) {
 
@@ -317,6 +318,7 @@ THREE.TDSLoader.prototype = {
 	 *
 	 * @method readMesh
 	 * @param {Dataview} data Dataview in use.
+	 * @return {Mesh} The parsed mesh.
 	 */
 	readMesh: function ( data ) {
 
@@ -460,7 +462,7 @@ THREE.TDSLoader.prototype = {
 
 			index.push( this.readWord( data ), this.readWord( data ), this.readWord( data ) );
 
-			var visibility = this.readWord( data );
+			this.readWord( data ); // visibility
 
 		}
 
@@ -513,6 +515,7 @@ THREE.TDSLoader.prototype = {
 	 *
 	 * @method readMap
 	 * @param {Dataview} data Dataview in use.
+	 * @param {String} path Path for external resources.
 	 * @return {Texture} Texture read from this data chunk.
 	 */
 	readMap: function ( data, path ) {
@@ -578,7 +581,7 @@ THREE.TDSLoader.prototype = {
 	 */
 	readMaterialGroup: function ( data ) {
 
-		var chunk = this.readChunk( data );
+		this.readChunk( data );
 		var name = this.readString( data, 64 );
 		var numFaces = this.readWord( data );
 
@@ -708,7 +711,6 @@ THREE.TDSLoader.prototype = {
 	 * Reset dataview position.
 	 *
 	 * @method resetPosition
-	 * @param {DataView} data Dataview.
 	 */
 	resetPosition: function () {
 
@@ -908,220 +910,220 @@ THREE.TDSLoader.prototype = {
 	}
 };
 
-var NULL_CHUNK = 0x0000;
+// var NULL_CHUNK = 0x0000;
 var M3DMAGIC = 0x4D4D;
-var SMAGIC = 0x2D2D;
-var LMAGIC = 0x2D3D;
+// var SMAGIC = 0x2D2D;
+// var LMAGIC = 0x2D3D;
 var MLIBMAGIC = 0x3DAA;
-var MATMAGIC = 0x3DFF;
+// var MATMAGIC = 0x3DFF;
 var CMAGIC = 0xC23D;
 var M3D_VERSION = 0x0002;
-var M3D_KFVERSION = 0x0005;
+// var M3D_KFVERSION = 0x0005;
 var COLOR_F = 0x0010;
 var COLOR_24 = 0x0011;
 var LIN_COLOR_24 = 0x0012;
 var LIN_COLOR_F = 0x0013;
-var INT_PERCENTAGE = 0x0030;
-var FLOAT_PERCENTAGE = 0x0031;
+// var INT_PERCENTAGE = 0x0030;
+// var FLOAT_PERCENTAGE = 0x0031;
 var MDATA = 0x3D3D;
 var MESH_VERSION = 0x3D3E;
 var MASTER_SCALE = 0x0100;
-var LO_SHADOW_BIAS = 0x1400;
-var HI_SHADOW_BIAS = 0x1410;
-var SHADOW_MAP_SIZE = 0x1420;
-var SHADOW_SAMPLES = 0x1430;
-var SHADOW_RANGE = 0x1440;
-var SHADOW_FILTER = 0x1450;
-var RAY_BIAS = 0x1460;
-var O_CONSTS = 0x1500;
-var AMBIENT_LIGHT = 0x2100;
-var BIT_MAP = 0x1100;
-var SOLID_BGND = 0x1200;
-var V_GRADIENT = 0x1300;
-var USE_BIT_MAP = 0x1101;
-var USE_SOLID_BGND = 0x1201;
-var USE_V_GRADIENT = 0x1301;
-var FOG = 0x2200;
-var FOG_BGND = 0x2210;
-var LAYER_FOG = 0x2302;
-var DISTANCE_CUE = 0x2300;
-var DCUE_BGND = 0x2310;
-var USE_FOG = 0x2201;
-var USE_LAYER_FOG = 0x2303;
-var USE_DISTANCE_CUE = 0x2301;
+// var LO_SHADOW_BIAS = 0x1400;
+// var HI_SHADOW_BIAS = 0x1410;
+// var SHADOW_MAP_SIZE = 0x1420;
+// var SHADOW_SAMPLES = 0x1430;
+// var SHADOW_RANGE = 0x1440;
+// var SHADOW_FILTER = 0x1450;
+// var RAY_BIAS = 0x1460;
+// var O_CONSTS = 0x1500;
+// var AMBIENT_LIGHT = 0x2100;
+// var BIT_MAP = 0x1100;
+// var SOLID_BGND = 0x1200;
+// var V_GRADIENT = 0x1300;
+// var USE_BIT_MAP = 0x1101;
+// var USE_SOLID_BGND = 0x1201;
+// var USE_V_GRADIENT = 0x1301;
+// var FOG = 0x2200;
+// var FOG_BGND = 0x2210;
+// var LAYER_FOG = 0x2302;
+// var DISTANCE_CUE = 0x2300;
+// var DCUE_BGND = 0x2310;
+// var USE_FOG = 0x2201;
+// var USE_LAYER_FOG = 0x2303;
+// var USE_DISTANCE_CUE = 0x2301;
 var MAT_ENTRY = 0xAFFF;
 var MAT_NAME = 0xA000;
 var MAT_AMBIENT = 0xA010;
 var MAT_DIFFUSE = 0xA020;
 var MAT_SPECULAR = 0xA030;
 var MAT_SHININESS = 0xA040;
-var MAT_SHIN2PCT = 0xA041;
-var MAT_TRANSPARENCY = 0xA050;
-var MAT_XPFALL = 0xA052;
-var MAT_USE_XPFALL = 0xA240;
-var MAT_REFBLUR = 0xA053;
-var MAT_SHADING = 0xA100;
-var MAT_USE_REFBLUR = 0xA250;
-var MAT_SELF_ILLUM = 0xA084;
+// var MAT_SHIN2PCT = 0xA041;
+// var MAT_TRANSPARENCY = 0xA050;
+// var MAT_XPFALL = 0xA052;
+// var MAT_USE_XPFALL = 0xA240;
+// var MAT_REFBLUR = 0xA053;
+// var MAT_SHADING = 0xA100;
+// var MAT_USE_REFBLUR = 0xA250;
+// var MAT_SELF_ILLUM = 0xA084;
 var MAT_TWO_SIDE = 0xA081;
-var MAT_DECAL = 0xA082;
+// var MAT_DECAL = 0xA082;
 var MAT_ADDITIVE = 0xA083;
 var MAT_WIRE = 0xA085;
-var MAT_FACEMAP = 0xA088;
-var MAT_TRANSFALLOFF_IN = 0xA08A;
-var MAT_PHONGSOFT = 0xA08C;
-var MAT_WIREABS = 0xA08E;
+// var MAT_FACEMAP = 0xA088;
+// var MAT_TRANSFALLOFF_IN = 0xA08A;
+// var MAT_PHONGSOFT = 0xA08C;
+// var MAT_WIREABS = 0xA08E;
 var MAT_WIRE_SIZE = 0xA087;
 var MAT_TEXMAP = 0xA200;
-var MAT_SXP_TEXT_DATA = 0xA320;
-var MAT_TEXMASK = 0xA33E;
-var MAT_SXP_TEXTMASK_DATA = 0xA32A;
-var MAT_TEX2MAP = 0xA33A;
-var MAT_SXP_TEXT2_DATA = 0xA321;
-var MAT_TEX2MASK = 0xA340;
-var MAT_SXP_TEXT2MASK_DATA = 0xA32C;
+// var MAT_SXP_TEXT_DATA = 0xA320;
+// var MAT_TEXMASK = 0xA33E;
+// var MAT_SXP_TEXTMASK_DATA = 0xA32A;
+// var MAT_TEX2MAP = 0xA33A;
+// var MAT_SXP_TEXT2_DATA = 0xA321;
+// var MAT_TEX2MASK = 0xA340;
+// var MAT_SXP_TEXT2MASK_DATA = 0xA32C;
 var MAT_OPACMAP = 0xA210;
-var MAT_SXP_OPAC_DATA = 0xA322;
-var MAT_OPACMASK = 0xA342;
-var MAT_SXP_OPACMASK_DATA = 0xA32E;
+// var MAT_SXP_OPAC_DATA = 0xA322;
+// var MAT_OPACMASK = 0xA342;
+// var MAT_SXP_OPACMASK_DATA = 0xA32E;
 var MAT_BUMPMAP = 0xA230;
-var MAT_SXP_BUMP_DATA = 0xA324;
-var MAT_BUMPMASK = 0xA344;
-var MAT_SXP_BUMPMASK_DATA = 0xA330;
+// var MAT_SXP_BUMP_DATA = 0xA324;
+// var MAT_BUMPMASK = 0xA344;
+// var MAT_SXP_BUMPMASK_DATA = 0xA330;
 var MAT_SPECMAP = 0xA204;
-var MAT_SXP_SPEC_DATA = 0xA325;
-var MAT_SPECMASK = 0xA348;
-var MAT_SXP_SPECMASK_DATA = 0xA332;
-var MAT_SHINMAP = 0xA33C;
-var MAT_SXP_SHIN_DATA = 0xA326;
-var MAT_SHINMASK = 0xA346;
-var MAT_SXP_SHINMASK_DATA = 0xA334;
-var MAT_SELFIMAP = 0xA33D;
-var MAT_SXP_SELFI_DATA = 0xA328;
-var MAT_SELFIMASK = 0xA34A;
-var MAT_SXP_SELFIMASK_DATA = 0xA336;
-var MAT_REFLMAP = 0xA220;
-var MAT_REFLMASK = 0xA34C;
-var MAT_SXP_REFLMASK_DATA = 0xA338;
-var MAT_ACUBIC = 0xA310;
+// var MAT_SXP_SPEC_DATA = 0xA325;
+// var MAT_SPECMASK = 0xA348;
+// var MAT_SXP_SPECMASK_DATA = 0xA332;
+// var MAT_SHINMAP = 0xA33C;
+// var MAT_SXP_SHIN_DATA = 0xA326;
+// var MAT_SHINMASK = 0xA346;
+// var MAT_SXP_SHINMASK_DATA = 0xA334;
+// var MAT_SELFIMAP = 0xA33D;
+// var MAT_SXP_SELFI_DATA = 0xA328;
+// var MAT_SELFIMASK = 0xA34A;
+// var MAT_SXP_SELFIMASK_DATA = 0xA336;
+// var MAT_REFLMAP = 0xA220;
+// var MAT_REFLMASK = 0xA34C;
+// var MAT_SXP_REFLMASK_DATA = 0xA338;
+// var MAT_ACUBIC = 0xA310;
 var MAT_MAPNAME = 0xA300;
-var MAT_MAP_TILING = 0xA351;
-var MAT_MAP_TEXBLUR = 0xA353;
+// var MAT_MAP_TILING = 0xA351;
+// var MAT_MAP_TEXBLUR = 0xA353;
 var MAT_MAP_USCALE = 0xA354;
 var MAT_MAP_VSCALE = 0xA356;
 var MAT_MAP_UOFFSET = 0xA358;
 var MAT_MAP_VOFFSET = 0xA35A;
-var MAT_MAP_ANG = 0xA35C;
-var MAT_MAP_COL1 = 0xA360;
-var MAT_MAP_COL2 = 0xA362;
-var MAT_MAP_RCOL = 0xA364;
-var MAT_MAP_GCOL = 0xA366;
-var MAT_MAP_BCOL = 0xA368;
+// var MAT_MAP_ANG = 0xA35C;
+// var MAT_MAP_COL1 = 0xA360;
+// var MAT_MAP_COL2 = 0xA362;
+// var MAT_MAP_RCOL = 0xA364;
+// var MAT_MAP_GCOL = 0xA366;
+// var MAT_MAP_BCOL = 0xA368;
 var NAMED_OBJECT = 0x4000;
-var N_DIRECT_LIGHT = 0x4600;
-var DL_OFF = 0x4620;
-var DL_OUTER_RANGE = 0x465A;
-var DL_INNER_RANGE = 0x4659;
-var DL_MULTIPLIER = 0x465B;
-var DL_EXCLUDE = 0x4654;
-var DL_ATTENUATE = 0x4625;
-var DL_SPOTLIGHT = 0x4610;
-var DL_SPOT_ROLL = 0x4656;
-var DL_SHADOWED = 0x4630;
-var DL_LOCAL_SHADOW2 = 0x4641;
-var DL_SEE_CONE = 0x4650;
-var DL_SPOT_RECTANGULAR = 0x4651;
-var DL_SPOT_ASPECT = 0x4657;
-var DL_SPOT_PROJECTOR = 0x4653;
-var DL_SPOT_OVERSHOOT = 0x4652;
-var DL_RAY_BIAS = 0x4658;
-var DL_RAYSHAD = 0x4627;
-var N_CAMERA = 0x4700;
-var CAM_SEE_CONE = 0x4710;
-var CAM_RANGES = 0x4720;
-var OBJ_HIDDEN = 0x4010;
-var OBJ_VIS_LOFTER = 0x4011;
-var OBJ_DOESNT_CAST = 0x4012;
-var OBJ_DONT_RECVSHADOW = 0x4017;
-var OBJ_MATTE = 0x4013;
-var OBJ_FAST = 0x4014;
-var OBJ_PROCEDURAL = 0x4015;
-var OBJ_FROZEN = 0x4016;
+// var N_DIRECT_LIGHT = 0x4600;
+// var DL_OFF = 0x4620;
+// var DL_OUTER_RANGE = 0x465A;
+// var DL_INNER_RANGE = 0x4659;
+// var DL_MULTIPLIER = 0x465B;
+// var DL_EXCLUDE = 0x4654;
+// var DL_ATTENUATE = 0x4625;
+// var DL_SPOTLIGHT = 0x4610;
+// var DL_SPOT_ROLL = 0x4656;
+// var DL_SHADOWED = 0x4630;
+// var DL_LOCAL_SHADOW2 = 0x4641;
+// var DL_SEE_CONE = 0x4650;
+// var DL_SPOT_RECTANGULAR = 0x4651;
+// var DL_SPOT_ASPECT = 0x4657;
+// var DL_SPOT_PROJECTOR = 0x4653;
+// var DL_SPOT_OVERSHOOT = 0x4652;
+// var DL_RAY_BIAS = 0x4658;
+// var DL_RAYSHAD = 0x4627;
+// var N_CAMERA = 0x4700;
+// var CAM_SEE_CONE = 0x4710;
+// var CAM_RANGES = 0x4720;
+// var OBJ_HIDDEN = 0x4010;
+// var OBJ_VIS_LOFTER = 0x4011;
+// var OBJ_DOESNT_CAST = 0x4012;
+// var OBJ_DONT_RECVSHADOW = 0x4017;
+// var OBJ_MATTE = 0x4013;
+// var OBJ_FAST = 0x4014;
+// var OBJ_PROCEDURAL = 0x4015;
+// var OBJ_FROZEN = 0x4016;
 var N_TRI_OBJECT = 0x4100;
 var POINT_ARRAY = 0x4110;
-var POINT_FLAG_ARRAY = 0x4111;
+// var POINT_FLAG_ARRAY = 0x4111;
 var FACE_ARRAY = 0x4120;
 var MSH_MAT_GROUP = 0x4130;
-var SMOOTH_GROUP = 0x4150;
-var MSH_BOXMAP = 0x4190;
+// var SMOOTH_GROUP = 0x4150;
+// var MSH_BOXMAP = 0x4190;
 var TEX_VERTS = 0x4140;
 var MESH_MATRIX = 0x4160;
-var MESH_COLOR = 0x4165;
-var MESH_TEXTURE_INFO = 0x4170;
-var KFDATA = 0xB000;
-var KFHDR = 0xB00A;
-var KFSEG = 0xB008;
-var KFCURTIME = 0xB009;
-var AMBIENT_NODE_TAG = 0xB001;
-var OBJECT_NODE_TAG = 0xB002;
-var CAMERA_NODE_TAG = 0xB003;
-var TARGET_NODE_TAG = 0xB004;
-var LIGHT_NODE_TAG = 0xB005;
-var L_TARGET_NODE_TAG = 0xB006;
-var SPOTLIGHT_NODE_TAG = 0xB007;
-var NODE_ID = 0xB030;
-var NODE_HDR = 0xB010;
-var PIVOT = 0xB013;
-var INSTANCE_NAME = 0xB011;
-var MORPH_SMOOTH = 0xB015;
-var BOUNDBOX = 0xB014;
-var POS_TRACK_TAG = 0xB020;
-var COL_TRACK_TAG = 0xB025;
-var ROT_TRACK_TAG = 0xB021;
-var SCL_TRACK_TAG = 0xB022;
-var MORPH_TRACK_TAG = 0xB026;
-var FOV_TRACK_TAG = 0xB023;
-var ROLL_TRACK_TAG = 0xB024;
-var HOT_TRACK_TAG = 0xB027;
-var FALL_TRACK_TAG = 0xB028;
-var HIDE_TRACK_TAG = 0xB029;
-var POLY_2D = 0x5000;
-var SHAPE_OK = 0x5010;
-var SHAPE_NOT_OK = 0x5011;
-var SHAPE_HOOK = 0x5020;
-var PATH_3D = 0x6000;
-var PATH_MATRIX = 0x6005;
-var SHAPE_2D = 0x6010;
-var M_SCALE = 0x6020;
-var M_TWIST = 0x6030;
-var M_TEETER = 0x6040;
-var M_FIT = 0x6050;
-var M_BEVEL = 0x6060;
-var XZ_CURVE = 0x6070;
-var YZ_CURVE = 0x6080;
-var INTERPCT = 0x6090;
-var DEFORM_LIMIT = 0x60A0;
-var USE_CONTOUR = 0x6100;
-var USE_TWEEN = 0x6110;
-var USE_SCALE = 0x6120;
-var USE_TWIST = 0x6130;
-var USE_TEETER = 0x6140;
-var USE_FIT = 0x6150;
-var USE_BEVEL = 0x6160;
-var DEFAULT_VIEW = 0x3000;
-var VIEW_TOP = 0x3010;
-var VIEW_BOTTOM = 0x3020;
-var VIEW_LEFT = 0x3030;
-var VIEW_RIGHT = 0x3040;
-var VIEW_FRONT = 0x3050;
-var VIEW_BACK = 0x3060;
-var VIEW_USER = 0x3070;
-var VIEW_CAMERA = 0x3080;
-var VIEW_WINDOW = 0x3090;
-var VIEWPORT_LAYOUT_OLD = 0x7000;
-var VIEWPORT_DATA_OLD = 0x7010;
-var VIEWPORT_LAYOUT = 0x7001;
-var VIEWPORT_DATA = 0x7011;
-var VIEWPORT_DATA_3 = 0x7012;
-var VIEWPORT_SIZE = 0x7020;
-var NETWORK_VIEW = 0x7030;
+// var MESH_COLOR = 0x4165;
+// var MESH_TEXTURE_INFO = 0x4170;
+// var KFDATA = 0xB000;
+// var KFHDR = 0xB00A;
+// var KFSEG = 0xB008;
+// var KFCURTIME = 0xB009;
+// var AMBIENT_NODE_TAG = 0xB001;
+// var OBJECT_NODE_TAG = 0xB002;
+// var CAMERA_NODE_TAG = 0xB003;
+// var TARGET_NODE_TAG = 0xB004;
+// var LIGHT_NODE_TAG = 0xB005;
+// var L_TARGET_NODE_TAG = 0xB006;
+// var SPOTLIGHT_NODE_TAG = 0xB007;
+// var NODE_ID = 0xB030;
+// var NODE_HDR = 0xB010;
+// var PIVOT = 0xB013;
+// var INSTANCE_NAME = 0xB011;
+// var MORPH_SMOOTH = 0xB015;
+// var BOUNDBOX = 0xB014;
+// var POS_TRACK_TAG = 0xB020;
+// var COL_TRACK_TAG = 0xB025;
+// var ROT_TRACK_TAG = 0xB021;
+// var SCL_TRACK_TAG = 0xB022;
+// var MORPH_TRACK_TAG = 0xB026;
+// var FOV_TRACK_TAG = 0xB023;
+// var ROLL_TRACK_TAG = 0xB024;
+// var HOT_TRACK_TAG = 0xB027;
+// var FALL_TRACK_TAG = 0xB028;
+// var HIDE_TRACK_TAG = 0xB029;
+// var POLY_2D = 0x5000;
+// var SHAPE_OK = 0x5010;
+// var SHAPE_NOT_OK = 0x5011;
+// var SHAPE_HOOK = 0x5020;
+// var PATH_3D = 0x6000;
+// var PATH_MATRIX = 0x6005;
+// var SHAPE_2D = 0x6010;
+// var M_SCALE = 0x6020;
+// var M_TWIST = 0x6030;
+// var M_TEETER = 0x6040;
+// var M_FIT = 0x6050;
+// var M_BEVEL = 0x6060;
+// var XZ_CURVE = 0x6070;
+// var YZ_CURVE = 0x6080;
+// var INTERPCT = 0x6090;
+// var DEFORM_LIMIT = 0x60A0;
+// var USE_CONTOUR = 0x6100;
+// var USE_TWEEN = 0x6110;
+// var USE_SCALE = 0x6120;
+// var USE_TWIST = 0x6130;
+// var USE_TEETER = 0x6140;
+// var USE_FIT = 0x6150;
+// var USE_BEVEL = 0x6160;
+// var DEFAULT_VIEW = 0x3000;
+// var VIEW_TOP = 0x3010;
+// var VIEW_BOTTOM = 0x3020;
+// var VIEW_LEFT = 0x3030;
+// var VIEW_RIGHT = 0x3040;
+// var VIEW_FRONT = 0x3050;
+// var VIEW_BACK = 0x3060;
+// var VIEW_USER = 0x3070;
+// var VIEW_CAMERA = 0x3080;
+// var VIEW_WINDOW = 0x3090;
+// var VIEWPORT_LAYOUT_OLD = 0x7000;
+// var VIEWPORT_DATA_OLD = 0x7010;
+// var VIEWPORT_LAYOUT = 0x7001;
+// var VIEWPORT_DATA = 0x7011;
+// var VIEWPORT_DATA_3 = 0x7012;
+// var VIEWPORT_SIZE = 0x7020;
+// var NETWORK_VIEW = 0x7030;

+ 15 - 3
examples/js/loaders/VTKLoader.js

@@ -99,7 +99,13 @@ Object.assign( THREE.VTKLoader.prototype, THREE.EventDispatcher.prototype, {
 
 				var line = lines[ i ];
 
-				if ( inPointsSection ) {
+				if ( line.indexOf( 'DATASET' ) === 0 ) {
+
+					var dataset = line.split( ' ' )[ 1 ];
+
+					if ( dataset !== 'POLYDATA' ) throw new Error( 'Unsupported DATASET type: ' + dataset );
+
+				} else if ( inPointsSection ) {
 
 					// get the vertices
 					while ( ( result = pat3Floats.exec( line ) ) !== null ) {
@@ -354,7 +360,13 @@ Object.assign( THREE.VTKLoader.prototype, THREE.EventDispatcher.prototype, {
 				state = findString( buffer, index );
 				line = state.parsedString;
 
-				if ( line.indexOf( 'POINTS' ) === 0 ) {
+				if ( line.indexOf( 'DATASET' ) === 0 ) {
+
+					var dataset = line.split( ' ' )[ 1 ];
+
+					if ( dataset !== 'POLYDATA' ) throw new Error( 'Unsupported DATASET type: ' + dataset );
+
+				} else if ( line.indexOf( 'POINTS' ) === 0 ) {
 
 					vtk.push( line );
 					// Add the points
@@ -1117,7 +1129,7 @@ Object.assign( THREE.VTKLoader.prototype, THREE.EventDispatcher.prototype, {
 
 			} else {
 
-				// TODO for vtu,vti,and other xml formats
+				throw new Error( 'Unsupported DATASET type' );
 
 			}
 

+ 6 - 9
examples/js/QuickHull.js → examples/js/math/ConvexHull.js

@@ -5,14 +5,14 @@
  *
  */
 
-( function () {
+THREE.ConvexHull = ( function () {
 
 	var Visible = 0;
 	var Deleted = 1;
 
 	var v1 = new THREE.Vector3();
 
-	function QuickHull() {
+	function ConvexHull() {
 
 		this.tolerance = - 1;
 
@@ -36,19 +36,19 @@
 
 	}
 
-	Object.assign( QuickHull.prototype, {
+	Object.assign( ConvexHull.prototype, {
 
 		setFromPoints: function ( points ) {
 
 			if ( Array.isArray( points ) !== true ) {
 
-				console.error( 'THREE.QuickHull: Points parameter is not an array.' );
+				console.error( 'THREE.ConvexHull: Points parameter is not an array.' );
 
 			}
 
 			if ( points.length < 4 ) {
 
-				console.error( 'THREE.QuickHull: The algorithm needs at least four points.' );
+				console.error( 'THREE.ConvexHull: The algorithm needs at least four points.' );
 
 			}
 
@@ -1311,9 +1311,6 @@
 
 	} );
 
-	// export
-
-	THREE.QuickHull = QuickHull;
-
+	return ConvexHull;
 
 } )();

+ 72 - 0
examples/js/math/ImprovedNoise.js

@@ -0,0 +1,72 @@
+// http://mrl.nyu.edu/~perlin/noise/
+
+THREE.ImprovedNoise = function () {
+
+	var p = [ 151, 160, 137, 91, 90, 15, 131, 13, 201, 95, 96, 53, 194, 233, 7, 225, 140, 36, 103, 30, 69, 142, 8, 99, 37, 240, 21, 10,
+		 23, 190, 6, 148, 247, 120, 234, 75, 0, 26, 197, 62, 94, 252, 219, 203, 117, 35, 11, 32, 57, 177, 33, 88, 237, 149, 56, 87,
+		 174, 20, 125, 136, 171, 168, 68, 175, 74, 165, 71, 134, 139, 48, 27, 166, 77, 146, 158, 231, 83, 111, 229, 122, 60, 211,
+		 133, 230, 220, 105, 92, 41, 55, 46, 245, 40, 244, 102, 143, 54, 65, 25, 63, 161, 1, 216, 80, 73, 209, 76, 132, 187, 208,
+		 89, 18, 169, 200, 196, 135, 130, 116, 188, 159, 86, 164, 100, 109, 198, 173, 186, 3, 64, 52, 217, 226, 250, 124, 123, 5,
+		 202, 38, 147, 118, 126, 255, 82, 85, 212, 207, 206, 59, 227, 47, 16, 58, 17, 182, 189, 28, 42, 223, 183, 170, 213, 119,
+		 248, 152, 2, 44, 154, 163, 70, 221, 153, 101, 155, 167, 43, 172, 9, 129, 22, 39, 253, 19, 98, 108, 110, 79, 113, 224, 232,
+		 178, 185, 112, 104, 218, 246, 97, 228, 251, 34, 242, 193, 238, 210, 144, 12, 191, 179, 162, 241, 81, 51, 145, 235, 249,
+		 14, 239, 107, 49, 192, 214, 31, 181, 199, 106, 157, 184, 84, 204, 176, 115, 121, 50, 45, 127, 4, 150, 254, 138, 236, 205,
+		 93, 222, 114, 67, 29, 24, 72, 243, 141, 128, 195, 78, 66, 215, 61, 156, 180 ];
+
+	for ( var i = 0; i < 256; i ++ ) {
+
+		p[ 256 + i ] = p[ i ];
+
+	}
+
+	function fade( t ) {
+
+		return t * t * t * ( t * ( t * 6 - 15 ) + 10 );
+
+	}
+
+	function lerp( t, a, b ) {
+
+		return a + t * ( b - a );
+
+	}
+
+	function grad( hash, x, y, z ) {
+
+		var h = hash & 15;
+		var u = h < 8 ? x : y, v = h < 4 ? y : h == 12 || h == 14 ? x : z;
+		return ( ( h & 1 ) == 0 ? u : - u ) + ( ( h & 2 ) == 0 ? v : - v );
+
+	}
+
+	return {
+
+		noise: function ( x, y, z ) {
+
+			var floorX = Math.floor( x ), floorY = Math.floor( y ), floorZ = Math.floor( z );
+
+			var X = floorX & 255, Y = floorY & 255, Z = floorZ & 255;
+
+			x -= floorX;
+			y -= floorY;
+			z -= floorZ;
+
+			var xMinus1 = x - 1, yMinus1 = y - 1, zMinus1 = z - 1;
+
+			var u = fade( x ), v = fade( y ), w = fade( z );
+
+			var A = p[ X ] + Y, AA = p[ A ] + Z, AB = p[ A + 1 ] + Z, B = p[ X + 1 ] + Y, BA = p[ B ] + Z, BB = p[ B + 1 ] + Z;
+
+			return lerp( w, lerp( v, lerp( u, grad( p[ AA ], x, y, z ),
+				grad( p[ BA ], xMinus1, y, z ) ),
+			lerp( u, grad( p[ AB ], x, yMinus1, z ),
+				grad( p[ BB ], xMinus1, yMinus1, z ) ) ),
+			lerp( v, lerp( u, grad( p[ AA + 1 ], x, y, zMinus1 ),
+				grad( p[ BA + 1 ], xMinus1, y, z - 1 ) ),
+			lerp( u, grad( p[ AB + 1 ], x, yMinus1, zMinus1 ),
+				grad( p[ BB + 1 ], xMinus1, yMinus1, zMinus1 ) ) ) );
+
+		}
+	};
+
+};

+ 0 - 15
examples/js/math/Lut.js

@@ -10,20 +10,6 @@ THREE.Lut = function ( colormap, numberofcolors ) {
 
 };
 
-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,
@@ -189,7 +175,6 @@ THREE.Lut.prototype = {
 	}
 };
 
-
 THREE.ColorMapKeywords = {
 
 	"rainbow": [[ 0.0, 0x0000FF ], [ 0.2, 0x00FFFF ], [ 0.5, 0x00FF00 ], [ 0.8, 0xFFFF00 ], [ 1.0, 0xFF0000 ]],

+ 405 - 0
examples/js/math/SimplexNoise.js

@@ -0,0 +1,405 @@
+// Ported from Stefan Gustavson's java implementation
+// http://staffwww.itn.liu.se/~stegu/simplexnoise/simplexnoise.pdf
+// Read Stefan's excellent paper for details on how this code works.
+//
+// Sean McCullough [email protected]
+//
+// Added 4D noise
+// Joshua Koo [email protected]
+
+/**
+ * You can pass in a random number generator object if you like.
+ * It is assumed to have a random() method.
+ */
+THREE.SimplexNoise = function ( r ) {
+
+	if ( r == undefined ) r = Math;
+	this.grad3 = [[ 1, 1, 0 ], [ - 1, 1, 0 ], [ 1, - 1, 0 ], [ - 1, - 1, 0 ],
+		[ 1, 0, 1 ], [ - 1, 0, 1 ], [ 1, 0, - 1 ], [ - 1, 0, - 1 ],
+		[ 0, 1, 1 ], [ 0, - 1, 1 ], [ 0, 1, - 1 ], [ 0, - 1, - 1 ]];
+
+	this.grad4 = [[ 0, 1, 1, 1 ], [ 0, 1, 1, - 1 ], [ 0, 1, - 1, 1 ], [ 0, 1, - 1, - 1 ],
+	     [ 0, - 1, 1, 1 ], [ 0, - 1, 1, - 1 ], [ 0, - 1, - 1, 1 ], [ 0, - 1, - 1, - 1 ],
+	     [ 1, 0, 1, 1 ], [ 1, 0, 1, - 1 ], [ 1, 0, - 1, 1 ], [ 1, 0, - 1, - 1 ],
+	     [ - 1, 0, 1, 1 ], [ - 1, 0, 1, - 1 ], [ - 1, 0, - 1, 1 ], [ - 1, 0, - 1, - 1 ],
+	     [ 1, 1, 0, 1 ], [ 1, 1, 0, - 1 ], [ 1, - 1, 0, 1 ], [ 1, - 1, 0, - 1 ],
+	     [ - 1, 1, 0, 1 ], [ - 1, 1, 0, - 1 ], [ - 1, - 1, 0, 1 ], [ - 1, - 1, 0, - 1 ],
+	     [ 1, 1, 1, 0 ], [ 1, 1, - 1, 0 ], [ 1, - 1, 1, 0 ], [ 1, - 1, - 1, 0 ],
+	     [ - 1, 1, 1, 0 ], [ - 1, 1, - 1, 0 ], [ - 1, - 1, 1, 0 ], [ - 1, - 1, - 1, 0 ]];
+
+	this.p = [];
+	for ( var i = 0; i < 256; i ++ ) {
+
+		this.p[ i ] = Math.floor( r.random() * 256 );
+
+	}
+	// To remove the need for index wrapping, double the permutation table length
+	this.perm = [];
+	for ( var i = 0; i < 512; i ++ ) {
+
+		this.perm[ i ] = this.p[ i & 255 ];
+
+	}
+
+	// A lookup table to traverse the simplex around a given point in 4D.
+	// Details can be found where this table is used, in the 4D noise method.
+	this.simplex = [
+		[ 0, 1, 2, 3 ], [ 0, 1, 3, 2 ], [ 0, 0, 0, 0 ], [ 0, 2, 3, 1 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 1, 2, 3, 0 ],
+		[ 0, 2, 1, 3 ], [ 0, 0, 0, 0 ], [ 0, 3, 1, 2 ], [ 0, 3, 2, 1 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 1, 3, 2, 0 ],
+		[ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ],
+		[ 1, 2, 0, 3 ], [ 0, 0, 0, 0 ], [ 1, 3, 0, 2 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 2, 3, 0, 1 ], [ 2, 3, 1, 0 ],
+		[ 1, 0, 2, 3 ], [ 1, 0, 3, 2 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 2, 0, 3, 1 ], [ 0, 0, 0, 0 ], [ 2, 1, 3, 0 ],
+		[ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ],
+		[ 2, 0, 1, 3 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 3, 0, 1, 2 ], [ 3, 0, 2, 1 ], [ 0, 0, 0, 0 ], [ 3, 1, 2, 0 ],
+		[ 2, 1, 0, 3 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 3, 1, 0, 2 ], [ 0, 0, 0, 0 ], [ 3, 2, 0, 1 ], [ 3, 2, 1, 0 ]];
+
+};
+
+THREE.SimplexNoise.prototype.dot = function ( g, x, y ) {
+
+	return g[ 0 ] * x + g[ 1 ] * y;
+
+};
+
+THREE.SimplexNoise.prototype.dot3 = function ( g, x, y, z ) {
+
+	return g[ 0 ] * x + g[ 1 ] * y + g[ 2 ] * z;
+
+};
+
+THREE.SimplexNoise.prototype.dot4 = function ( g, x, y, z, w ) {
+
+	return g[ 0 ] * x + g[ 1 ] * y + g[ 2 ] * z + g[ 3 ] * w;
+
+};
+
+THREE.SimplexNoise.prototype.noise = function ( xin, yin ) {
+
+	var n0, n1, n2; // Noise contributions from the three corners
+	// Skew the input space to determine which simplex cell we're in
+	var F2 = 0.5 * ( Math.sqrt( 3.0 ) - 1.0 );
+	var s = ( xin + yin ) * F2; // Hairy factor for 2D
+	var i = Math.floor( xin + s );
+	var j = Math.floor( yin + s );
+	var G2 = ( 3.0 - Math.sqrt( 3.0 ) ) / 6.0;
+	var t = ( i + j ) * G2;
+	var X0 = i - t; // Unskew the cell origin back to (x,y) space
+	var Y0 = j - t;
+	var x0 = xin - X0; // The x,y distances from the cell origin
+	var y0 = yin - Y0;
+	// For the 2D case, the simplex shape is an equilateral triangle.
+	// Determine which simplex we are in.
+	var i1, j1; // Offsets for second (middle) corner of simplex in (i,j) coords
+	if ( x0 > y0 ) {
+
+		i1 = 1; j1 = 0;
+
+		// lower triangle, XY order: (0,0)->(1,0)->(1,1)
+
+	}	else {
+
+		i1 = 0; j1 = 1;
+
+	} // upper triangle, YX order: (0,0)->(0,1)->(1,1)
+	// A step of (1,0) in (i,j) means a step of (1-c,-c) in (x,y), and
+	// a step of (0,1) in (i,j) means a step of (-c,1-c) in (x,y), where
+	// c = (3-sqrt(3))/6
+	var x1 = x0 - i1 + G2; // Offsets for middle corner in (x,y) unskewed coords
+	var y1 = y0 - j1 + G2;
+	var x2 = x0 - 1.0 + 2.0 * G2; // Offsets for last corner in (x,y) unskewed coords
+	var y2 = y0 - 1.0 + 2.0 * G2;
+	// Work out the hashed gradient indices of the three simplex corners
+	var ii = i & 255;
+	var jj = j & 255;
+	var gi0 = this.perm[ ii + this.perm[ jj ] ] % 12;
+	var gi1 = this.perm[ ii + i1 + this.perm[ jj + j1 ] ] % 12;
+	var gi2 = this.perm[ ii + 1 + this.perm[ jj + 1 ] ] % 12;
+	// Calculate the contribution from the three corners
+	var t0 = 0.5 - x0 * x0 - y0 * y0;
+	if ( t0 < 0 ) n0 = 0.0;
+	else {
+
+		t0 *= t0;
+		n0 = t0 * t0 * this.dot( this.grad3[ gi0 ], x0, y0 ); // (x,y) of grad3 used for 2D gradient
+
+	}
+	var t1 = 0.5 - x1 * x1 - y1 * y1;
+	if ( t1 < 0 ) n1 = 0.0;
+	else {
+
+		t1 *= t1;
+		n1 = t1 * t1 * this.dot( this.grad3[ gi1 ], x1, y1 );
+
+	}
+	var t2 = 0.5 - x2 * x2 - y2 * y2;
+	if ( t2 < 0 ) n2 = 0.0;
+	else {
+
+		t2 *= t2;
+		n2 = t2 * t2 * this.dot( this.grad3[ gi2 ], x2, y2 );
+
+	}
+	// Add contributions from each corner to get the final noise value.
+	// The result is scaled to return values in the interval [-1,1].
+	return 70.0 * ( n0 + n1 + n2 );
+
+};
+
+// 3D simplex noise
+THREE.SimplexNoise.prototype.noise3d = function ( xin, yin, zin ) {
+
+	var n0, n1, n2, n3; // Noise contributions from the four corners
+	// Skew the input space to determine which simplex cell we're in
+	var F3 = 1.0 / 3.0;
+	var s = ( xin + yin + zin ) * F3; // Very nice and simple skew factor for 3D
+	var i = Math.floor( xin + s );
+	var j = Math.floor( yin + s );
+	var k = Math.floor( zin + s );
+	var G3 = 1.0 / 6.0; // Very nice and simple unskew factor, too
+	var t = ( i + j + k ) * G3;
+	var X0 = i - t; // Unskew the cell origin back to (x,y,z) space
+	var Y0 = j - t;
+	var Z0 = k - t;
+	var x0 = xin - X0; // The x,y,z distances from the cell origin
+	var y0 = yin - Y0;
+	var z0 = zin - Z0;
+	// For the 3D case, the simplex shape is a slightly irregular tetrahedron.
+	// Determine which simplex we are in.
+	var i1, j1, k1; // Offsets for second corner of simplex in (i,j,k) coords
+	var i2, j2, k2; // Offsets for third corner of simplex in (i,j,k) coords
+	if ( x0 >= y0 ) {
+
+		if ( y0 >= z0 ) {
+
+			i1 = 1; j1 = 0; k1 = 0; i2 = 1; j2 = 1; k2 = 0;
+
+			// X Y Z order
+
+		} else if ( x0 >= z0 ) {
+
+			i1 = 1; j1 = 0; k1 = 0; i2 = 1; j2 = 0; k2 = 1;
+
+			// X Z Y order
+
+		} else {
+
+			i1 = 0; j1 = 0; k1 = 1; i2 = 1; j2 = 0; k2 = 1;
+
+		} // Z X Y order
+
+	} else { // x0<y0
+
+		if ( y0 < z0 ) {
+
+			i1 = 0; j1 = 0; k1 = 1; i2 = 0; j2 = 1; k2 = 1;
+
+			// Z Y X order
+
+		} else if ( x0 < z0 ) {
+
+			i1 = 0; j1 = 1; k1 = 0; i2 = 0; j2 = 1; k2 = 1;
+
+			// Y Z X order
+
+		} else {
+
+			i1 = 0; j1 = 1; k1 = 0; i2 = 1; j2 = 1; k2 = 0;
+
+		} // Y X Z order
+
+	}
+	// A step of (1,0,0) in (i,j,k) means a step of (1-c,-c,-c) in (x,y,z),
+	// a step of (0,1,0) in (i,j,k) means a step of (-c,1-c,-c) in (x,y,z), and
+	// a step of (0,0,1) in (i,j,k) means a step of (-c,-c,1-c) in (x,y,z), where
+	// c = 1/6.
+	var x1 = x0 - i1 + G3; // Offsets for second corner in (x,y,z) coords
+	var y1 = y0 - j1 + G3;
+	var z1 = z0 - k1 + G3;
+	var x2 = x0 - i2 + 2.0 * G3; // Offsets for third corner in (x,y,z) coords
+	var y2 = y0 - j2 + 2.0 * G3;
+	var z2 = z0 - k2 + 2.0 * G3;
+	var x3 = x0 - 1.0 + 3.0 * G3; // Offsets for last corner in (x,y,z) coords
+	var y3 = y0 - 1.0 + 3.0 * G3;
+	var z3 = z0 - 1.0 + 3.0 * G3;
+	// Work out the hashed gradient indices of the four simplex corners
+	var ii = i & 255;
+	var jj = j & 255;
+	var kk = k & 255;
+	var gi0 = this.perm[ ii + this.perm[ jj + this.perm[ kk ] ] ] % 12;
+	var gi1 = this.perm[ ii + i1 + this.perm[ jj + j1 + this.perm[ kk + k1 ] ] ] % 12;
+	var gi2 = this.perm[ ii + i2 + this.perm[ jj + j2 + this.perm[ kk + k2 ] ] ] % 12;
+	var gi3 = this.perm[ ii + 1 + this.perm[ jj + 1 + this.perm[ kk + 1 ] ] ] % 12;
+	// Calculate the contribution from the four corners
+	var t0 = 0.6 - x0 * x0 - y0 * y0 - z0 * z0;
+	if ( t0 < 0 ) n0 = 0.0;
+	else {
+
+		t0 *= t0;
+		n0 = t0 * t0 * this.dot3( this.grad3[ gi0 ], x0, y0, z0 );
+
+	}
+	var t1 = 0.6 - x1 * x1 - y1 * y1 - z1 * z1;
+	if ( t1 < 0 ) n1 = 0.0;
+	else {
+
+		t1 *= t1;
+		n1 = t1 * t1 * this.dot3( this.grad3[ gi1 ], x1, y1, z1 );
+
+	}
+	var t2 = 0.6 - x2 * x2 - y2 * y2 - z2 * z2;
+	if ( t2 < 0 ) n2 = 0.0;
+	else {
+
+		t2 *= t2;
+		n2 = t2 * t2 * this.dot3( this.grad3[ gi2 ], x2, y2, z2 );
+
+	}
+	var t3 = 0.6 - x3 * x3 - y3 * y3 - z3 * z3;
+	if ( t3 < 0 ) n3 = 0.0;
+	else {
+
+		t3 *= t3;
+		n3 = t3 * t3 * this.dot3( this.grad3[ gi3 ], x3, y3, z3 );
+
+	}
+	// Add contributions from each corner to get the final noise value.
+	// The result is scaled to stay just inside [-1,1]
+	return 32.0 * ( n0 + n1 + n2 + n3 );
+
+};
+
+// 4D simplex noise
+THREE.SimplexNoise.prototype.noise4d = function ( x, y, z, w ) {
+
+	// For faster and easier lookups
+	var grad4 = this.grad4;
+	var simplex = this.simplex;
+	var perm = this.perm;
+
+	// The skewing and unskewing factors are hairy again for the 4D case
+	var F4 = ( Math.sqrt( 5.0 ) - 1.0 ) / 4.0;
+	var G4 = ( 5.0 - Math.sqrt( 5.0 ) ) / 20.0;
+	var n0, n1, n2, n3, n4; // Noise contributions from the five corners
+	// Skew the (x,y,z,w) space to determine which cell of 24 simplices we're in
+	var s = ( x + y + z + w ) * F4; // Factor for 4D skewing
+	var i = Math.floor( x + s );
+	var j = Math.floor( y + s );
+	var k = Math.floor( z + s );
+	var l = Math.floor( w + s );
+	var t = ( i + j + k + l ) * G4; // Factor for 4D unskewing
+	var X0 = i - t; // Unskew the cell origin back to (x,y,z,w) space
+	var Y0 = j - t;
+	var Z0 = k - t;
+	var W0 = l - t;
+	var x0 = x - X0; // The x,y,z,w distances from the cell origin
+	var y0 = y - Y0;
+	var z0 = z - Z0;
+	var w0 = w - W0;
+
+	// For the 4D case, the simplex is a 4D shape I won't even try to describe.
+	// To find out which of the 24 possible simplices we're in, we need to
+	// determine the magnitude ordering of x0, y0, z0 and w0.
+	// The method below is a good way of finding the ordering of x,y,z,w and
+	// then find the correct traversal order for the simplex we’re in.
+	// First, six pair-wise comparisons are performed between each possible pair
+	// of the four coordinates, and the results are used to add up binary bits
+	// for an integer index.
+	var c1 = ( x0 > y0 ) ? 32 : 0;
+	var c2 = ( x0 > z0 ) ? 16 : 0;
+	var c3 = ( y0 > z0 ) ? 8 : 0;
+	var c4 = ( x0 > w0 ) ? 4 : 0;
+	var c5 = ( y0 > w0 ) ? 2 : 0;
+	var c6 = ( z0 > w0 ) ? 1 : 0;
+	var c = c1 + c2 + c3 + c4 + c5 + c6;
+	var i1, j1, k1, l1; // The integer offsets for the second simplex corner
+	var i2, j2, k2, l2; // The integer offsets for the third simplex corner
+	var i3, j3, k3, l3; // The integer offsets for the fourth simplex corner
+	// simplex[c] is a 4-vector with the numbers 0, 1, 2 and 3 in some order.
+	// Many values of c will never occur, since e.g. x>y>z>w makes x<z, y<w and x<w
+	// impossible. Only the 24 indices which have non-zero entries make any sense.
+	// We use a thresholding to set the coordinates in turn from the largest magnitude.
+	// The number 3 in the "simplex" array is at the position of the largest coordinate.
+	i1 = simplex[ c ][ 0 ] >= 3 ? 1 : 0;
+	j1 = simplex[ c ][ 1 ] >= 3 ? 1 : 0;
+	k1 = simplex[ c ][ 2 ] >= 3 ? 1 : 0;
+	l1 = simplex[ c ][ 3 ] >= 3 ? 1 : 0;
+	// The number 2 in the "simplex" array is at the second largest coordinate.
+	i2 = simplex[ c ][ 0 ] >= 2 ? 1 : 0;
+	j2 = simplex[ c ][ 1 ] >= 2 ? 1 : 0; k2 = simplex[ c ][ 2 ] >= 2 ? 1 : 0;
+	l2 = simplex[ c ][ 3 ] >= 2 ? 1 : 0;
+	// The number 1 in the "simplex" array is at the second smallest coordinate.
+	i3 = simplex[ c ][ 0 ] >= 1 ? 1 : 0;
+	j3 = simplex[ c ][ 1 ] >= 1 ? 1 : 0;
+	k3 = simplex[ c ][ 2 ] >= 1 ? 1 : 0;
+	l3 = simplex[ c ][ 3 ] >= 1 ? 1 : 0;
+	// The fifth corner has all coordinate offsets = 1, so no need to look that up.
+	var x1 = x0 - i1 + G4; // Offsets for second corner in (x,y,z,w) coords
+	var y1 = y0 - j1 + G4;
+	var z1 = z0 - k1 + G4;
+	var w1 = w0 - l1 + G4;
+	var x2 = x0 - i2 + 2.0 * G4; // Offsets for third corner in (x,y,z,w) coords
+	var y2 = y0 - j2 + 2.0 * G4;
+	var z2 = z0 - k2 + 2.0 * G4;
+	var w2 = w0 - l2 + 2.0 * G4;
+	var x3 = x0 - i3 + 3.0 * G4; // Offsets for fourth corner in (x,y,z,w) coords
+	var y3 = y0 - j3 + 3.0 * G4;
+	var z3 = z0 - k3 + 3.0 * G4;
+	var w3 = w0 - l3 + 3.0 * G4;
+	var x4 = x0 - 1.0 + 4.0 * G4; // Offsets for last corner in (x,y,z,w) coords
+	var y4 = y0 - 1.0 + 4.0 * G4;
+	var z4 = z0 - 1.0 + 4.0 * G4;
+	var w4 = w0 - 1.0 + 4.0 * G4;
+	// Work out the hashed gradient indices of the five simplex corners
+	var ii = i & 255;
+	var jj = j & 255;
+	var kk = k & 255;
+	var ll = l & 255;
+	var gi0 = perm[ ii + perm[ jj + perm[ kk + perm[ ll ] ] ] ] % 32;
+	var gi1 = perm[ ii + i1 + perm[ jj + j1 + perm[ kk + k1 + perm[ ll + l1 ] ] ] ] % 32;
+	var gi2 = perm[ ii + i2 + perm[ jj + j2 + perm[ kk + k2 + perm[ ll + l2 ] ] ] ] % 32;
+	var gi3 = perm[ ii + i3 + perm[ jj + j3 + perm[ kk + k3 + perm[ ll + l3 ] ] ] ] % 32;
+	var gi4 = perm[ ii + 1 + perm[ jj + 1 + perm[ kk + 1 + perm[ ll + 1 ] ] ] ] % 32;
+	// Calculate the contribution from the five corners
+	var t0 = 0.6 - x0 * x0 - y0 * y0 - z0 * z0 - w0 * w0;
+	if ( t0 < 0 ) n0 = 0.0;
+	else {
+
+		t0 *= t0;
+		n0 = t0 * t0 * this.dot4( grad4[ gi0 ], x0, y0, z0, w0 );
+
+	}
+	var t1 = 0.6 - x1 * x1 - y1 * y1 - z1 * z1 - w1 * w1;
+	if ( t1 < 0 ) n1 = 0.0;
+	else {
+
+		t1 *= t1;
+		n1 = t1 * t1 * this.dot4( grad4[ gi1 ], x1, y1, z1, w1 );
+
+	}
+	var t2 = 0.6 - x2 * x2 - y2 * y2 - z2 * z2 - w2 * w2;
+	if ( t2 < 0 ) n2 = 0.0;
+	else {
+
+		t2 *= t2;
+		n2 = t2 * t2 * this.dot4( grad4[ gi2 ], x2, y2, z2, w2 );
+
+	} var t3 = 0.6 - x3 * x3 - y3 * y3 - z3 * z3 - w3 * w3;
+	if ( t3 < 0 ) n3 = 0.0;
+	else {
+
+		t3 *= t3;
+		n3 = t3 * t3 * this.dot4( grad4[ gi3 ], x3, y3, z3, w3 );
+
+	}
+	var t4 = 0.6 - x4 * x4 - y4 * y4 - z4 * z4 - w4 * w4;
+	if ( t4 < 0 ) n4 = 0.0;
+	else {
+
+		t4 *= t4;
+		n4 = t4 * t4 * this.dot4( grad4[ gi4 ], x4, y4, z4, w4 );
+
+	}
+	// Sum up and scale the result to cover the range [-1,1]
+	return 27.0 * ( n0 + n1 + n2 + n3 + n4 );
+
+};

+ 4 - 5
examples/js/modifiers/SubdivisionModifier.js

@@ -53,7 +53,6 @@ THREE.SubdivisionModifier.prototype.modify = function ( geometry ) {
 ( function () {
 
 	// Some constants
-	var WARNINGS = ! true; // Set to true for development
 	var ABC = [ 'a', 'b', 'c' ];
 
 
@@ -215,7 +214,7 @@ THREE.SubdivisionModifier.prototype.modify = function ( geometry ) {
 
 				if ( connectedFaces != 1 ) {
 
-					if ( WARNINGS ) console.warn( 'Subdivision Modifier: Number of connected faces != 2, is: ', connectedFaces, currentEdge );
+					// console.warn( 'Subdivision Modifier: Number of connected faces != 2, is: ', connectedFaces, currentEdge );
 
 				}
 
@@ -292,7 +291,7 @@ THREE.SubdivisionModifier.prototype.modify = function ( geometry ) {
 
 				if ( n == 2 ) {
 
-					if ( WARNINGS ) console.warn( '2 connecting edges', connectingEdges );
+					// console.warn( '2 connecting edges', connectingEdges );
 					sourceVertexWeight = 3 / 4;
 					connectingVertexWeight = 1 / 8;
 
@@ -301,11 +300,11 @@ THREE.SubdivisionModifier.prototype.modify = function ( geometry ) {
 
 				} else if ( n == 1 ) {
 
-					if ( WARNINGS ) console.warn( 'only 1 connecting edge' );
+					// console.warn( 'only 1 connecting edge' );
 
 				} else if ( n == 0 ) {
 
-					if ( WARNINGS ) console.warn( '0 connecting edges' );
+					// console.warn( '0 connecting edges' );
 
 				}
 

+ 2 - 2
examples/js/modifiers/TessellateModifier.js

@@ -186,7 +186,7 @@ THREE.TessellateModifier.prototype.modify = function ( geometry ) {
 							var uvsTriA = [ uvA.clone(), uvM.clone(), uvC.clone() ];
 							var uvsTriB = [ uvM.clone(), uvB.clone(), uvC.clone() ];
 
-						// BC
+							// BC
 
 						} else if ( edge === 1 ) {
 
@@ -196,7 +196,7 @@ THREE.TessellateModifier.prototype.modify = function ( geometry ) {
 							var uvsTriA = [ uvA.clone(), uvB.clone(), uvM.clone() ];
 							var uvsTriB = [ uvM.clone(), uvC.clone(), uvA.clone() ];
 
-						// AC
+							// AC
 
 						} else {
 

+ 7 - 1
examples/js/nodes/core/FunctionNode.js

@@ -85,7 +85,13 @@ FunctionNode.prototype.generate = function ( builder, output ) {
 
 	}
 
-	while ( match = propertiesRegexp.exec( this.src ) ) {
+	var matches = [];
+
+	while ( match = propertiesRegexp.exec( this.src ) ) matches.push( match );
+
+	for ( var i = 0; i < matches.length; i++ ) {
+
+		var match = matches[i];
 
 		var prop = match[ 0 ],
 			isGlobal = this.isMethod ? ! this.getInputByName( prop ) : true,

+ 0 - 36
examples/js/objects/Fire.js

@@ -498,11 +498,9 @@ THREE.Fire.SourceShader = {
 
 	uniforms: {
 		'sourceMap': {
-			type: 't',
 			value: null
 		},
 		'densityMap': {
-			type: 't',
 			value: null
 		}
 	},
@@ -557,39 +555,30 @@ THREE.Fire.DiffuseShader = {
 
 	uniforms: {
 		'oneOverWidth': {
-			type: 'f',
 			value: null
 		},
 		'oneOverHeight': {
-			type: 'f',
 			value: null
 		},
 		'diffuse': {
-			type: 'f',
 			value: null
 		},
 		'viscosity': {
-			type: 'f',
 			value: null
 		},
 		'expansion': {
-			type: 'f',
 			value: null
 		},
 		'swirl': {
-			type: 'f',
 			value: null
 		},
 		'drag': {
-			type: 'f',
 			value: null
 		},
 		'burnRate': {
-			type: 'f',
 			value: null
 		},
 		'densityMap': {
-			type: 't',
 			value: null
 		}
 	},
@@ -674,23 +663,18 @@ THREE.Fire.DriftShader = {
 
 	uniforms: {
 		'oneOverWidth': {
-			type: 'f',
 			value: null
 		},
 		'oneOverHeight': {
-			type: 'f',
 			value: null
 		},
 		'windVector': {
-			type: 'v2',
 			value: new THREE.Vector2( 0.0, 0.0 )
 		},
 		'airSpeed': {
-			type: 'f',
 			value: null
 		},
 		'densityMap': {
-			type: 't',
 			value: null
 		}
 	},
@@ -759,15 +743,12 @@ THREE.Fire.ProjectionShader1 = {
 
 	uniforms: {
 		'oneOverWidth': {
-			type: 'f',
 			value: null
 		},
 		'oneOverHeight': {
-			type: 'f',
 			value: null
 		},
 		'densityMap': {
-			type: 't',
 			value: null
 		}
 	},
@@ -819,15 +800,12 @@ THREE.Fire.ProjectionShader2 = {
 
 	uniforms: {
 		'oneOverWidth': {
-			type: 'f',
 			value: null
 		},
 		'oneOverHeight': {
-			type: 'f',
 			value: null
 		},
 		'densityMap': {
-			type: 't',
 			value: null
 		}
 	},
@@ -880,19 +858,15 @@ THREE.Fire.ProjectionShader3 = {
 
 	uniforms: {
 		'oneOverWidth': {
-			type: 'f',
 			value: null
 		},
 		'oneOverHeight': {
-			type: 'f',
 			value: null
 		},
 		'densityMap': {
-			type: 't',
 			value: null
 		},
 		'projMap': {
-			type: 't',
 			value: null
 		}
 	},
@@ -950,23 +924,18 @@ THREE.Fire.ColorShader = {
 
 	uniforms: {
 		'color1': {
-			type: 'c',
 			value: null
 		},
 		'color2': {
-			type: 'c',
 			value: null
 		},
 		'color3': {
-			type: 'c',
 			value: null
 		},
 		'colorBias': {
-			type: 'f',
 			value: null
 		},
 		'densityMap': {
-			type: 't',
 			value: null
 		}
 	},
@@ -1014,23 +983,18 @@ THREE.Fire.DebugShader = {
 
 	uniforms: {
 		'color1': {
-			type: 'c',
 			value: null
 		},
 		'color2': {
-			type: 'c',
 			value: null
 		},
 		'color3': {
-			type: 'c',
 			value: null
 		},
 		'colorBias': {
-			type: 'f',
 			value: null
 		},
 		'densityMap': {
-			type: 't',
 			value: null
 		}
 	},

+ 4 - 0
examples/js/objects/ReflectorRTT.js

@@ -1,3 +1,7 @@
+/**
+ * RTT version
+ */
+
 THREE.ReflectorRTT = function ( geometry, options ) {
 
 	THREE.Reflector.call( this, geometry, options );

+ 10 - 10
examples/js/objects/ShadowMesh.js

@@ -42,23 +42,23 @@ THREE.ShadowMesh.prototype.update = function () {
 
 		var sme = shadowMatrix.elements;
 
-		sme[ 0 ]  = dot - lightPosition4D.x * plane.normal.x;
-		sme[ 4 ]  = - lightPosition4D.x * plane.normal.y;
-		sme[ 8 ]  = - lightPosition4D.x * plane.normal.z;
+		sme[ 0 ] = dot - lightPosition4D.x * plane.normal.x;
+		sme[ 4 ] = - lightPosition4D.x * plane.normal.y;
+		sme[ 8 ] = - lightPosition4D.x * plane.normal.z;
 		sme[ 12 ] = - lightPosition4D.x * - plane.constant;
 
-		sme[ 1 ]  = - lightPosition4D.y * plane.normal.x;
-		sme[ 5 ]  = dot - lightPosition4D.y * plane.normal.y;
-		sme[ 9 ]  = - lightPosition4D.y * plane.normal.z;
+		sme[ 1 ] = - lightPosition4D.y * plane.normal.x;
+		sme[ 5 ] = dot - lightPosition4D.y * plane.normal.y;
+		sme[ 9 ] = - lightPosition4D.y * plane.normal.z;
 		sme[ 13 ] = - lightPosition4D.y * - plane.constant;
 
-		sme[ 2 ]  = - lightPosition4D.z * plane.normal.x;
-		sme[ 6 ]  = - lightPosition4D.z * plane.normal.y;
+		sme[ 2 ] = - lightPosition4D.z * plane.normal.x;
+		sme[ 6 ] = - lightPosition4D.z * plane.normal.y;
 		sme[ 10 ] = dot - lightPosition4D.z * plane.normal.z;
 		sme[ 14 ] = - lightPosition4D.z * - plane.constant;
 
-		sme[ 3 ]  = - lightPosition4D.w * plane.normal.x;
-		sme[ 7 ]  = - lightPosition4D.w * plane.normal.y;
+		sme[ 3 ] = - lightPosition4D.w * plane.normal.x;
+		sme[ 7 ] = - lightPosition4D.w * plane.normal.y;
 		sme[ 11 ] = - lightPosition4D.w * plane.normal.z;
 		sme[ 15 ] = dot - lightPosition4D.w * - plane.constant;
 

+ 2 - 4
examples/js/postprocessing/EffectComposer.js

@@ -58,7 +58,7 @@ THREE.EffectComposer = function ( renderer, renderTarget ) {
 
 	this.copyPass = new THREE.ShaderPass( THREE.CopyShader );
 
-	this._previousFrameTime = Date.now();
+	this.clock = new THREE.Clock();
 
 };
 
@@ -109,12 +109,10 @@ Object.assign( THREE.EffectComposer.prototype, {
 
 		if ( deltaTime === undefined ) {
 
-			deltaTime = ( Date.now() - this._previousFrameTime ) * 0.001;
+			deltaTime = this.clock.getDelta();
 
 		}
 
-		this._previousFrameTime = Date.now();
-
 		var currentRenderTarget = this.renderer.getRenderTarget();
 
 		var maskActive = false;

+ 2 - 2
examples/js/postprocessing/SSAOPass.js

@@ -357,13 +357,13 @@ THREE.SSAOPass.prototype = Object.assign( Object.create( THREE.Pass.prototype ),
 
 		var width = 4, height = 4;
 
-		if ( SimplexNoise === undefined ) {
+		if ( THREE.SimplexNoise === undefined ) {
 
 			console.error( 'THREE.SSAOPass: The pass relies on THREE.SimplexNoise.' );
 
 		}
 
-		var simplex = new SimplexNoise();
+		var simplex = new THREE.SimplexNoise();
 
 		var size = width * height;
 		var data = new Float32Array( size * 4 );

+ 3 - 4
examples/js/renderers/RaytracingRenderer.js

@@ -124,8 +124,6 @@ THREE.RaytracingRenderer = function ( parameters ) {
 		canvasWidth = canvas.width;
 		canvasHeight = canvas.height;
 
-		context.fillStyle = 'white';
-
 		pool.forEach( updateSettings );
 
 	};
@@ -175,7 +173,6 @@ THREE.RaytracingRenderer = function ( parameters ) {
 		} );
 
 		context.fillStyle = '#' + worker.color;
-
 		context.fillRect( blockX, blockY, blockSize, blockSize );
 
 	}
@@ -246,7 +243,9 @@ THREE.RaytracingRenderer = function ( parameters ) {
 
 		} );
 
-		context.clearRect( 0, 0, canvasWidth, canvasHeight );
+		context.fillStyle = clearColor.getStyle();
+		context.fillRect( 0, 0, canvasWidth, canvasHeight );
+
 		reallyThen = Date.now();
 
 		xblocks = Math.ceil( canvasWidth / blockSize );

+ 28 - 0
examples/js/renderers/SVGRenderer.js

@@ -57,6 +57,8 @@ THREE.SVGRenderer = function () {
 	this.sortObjects = true;
 	this.sortElements = true;
 
+	this.overdraw = 0.5;
+
 	this.info = {
 
 		render: {
@@ -227,6 +229,14 @@ THREE.SVGRenderer = function () {
 				_v2.positionScreen.x *= _svgWidthHalf; _v2.positionScreen.y *= - _svgHeightHalf;
 				_v3.positionScreen.x *= _svgWidthHalf; _v3.positionScreen.y *= - _svgHeightHalf;
 
+				if ( this.overdraw > 0 ) {
+
+					expand( _v1.positionScreen, _v2.positionScreen, this.overdraw );
+					expand( _v2.positionScreen, _v3.positionScreen, this.overdraw );
+					expand( _v3.positionScreen, _v1.positionScreen, this.overdraw );
+
+				}
+
 				_elemBox.setFromPoints( [
 					_v1.positionScreen,
 					_v2.positionScreen,
@@ -451,6 +461,24 @@ THREE.SVGRenderer = function () {
 
 	}
 
+	// Hide anti-alias gaps
+
+	function expand( v1, v2, pixels ) {
+
+		var x = v2.x - v1.x, y = v2.y - v1.y,
+			det = x * x + y * y, idet;
+
+		if ( det === 0 ) return;
+
+		idet = pixels / Math.sqrt( det );
+
+		x *= idet; y *= idet;
+
+		v2.x += x; v2.y += y;
+		v1.x -= x; v1.y -= y;
+
+	}
+
 	function addPath( style, path ) {
 
 		if ( _currentStyle === style ) {

+ 8 - 8
examples/js/renderers/WebGLDeferredRenderer.js

@@ -418,7 +418,7 @@ THREE.WebGLDeferredRenderer = function ( parameters ) {
 
 	}
 
-	function createDeferredNormalDepthMaterial( originalMaterial ) {
+	function createDeferredNormalDepthMaterial() {
 
 		var shader = ( _lightPrePass ) ? THREE.ShaderDeferred[ 'normalDepthShininess' ] : THREE.ShaderDeferred[ 'normalDepth' ];
 
@@ -448,7 +448,7 @@ THREE.WebGLDeferredRenderer = function ( parameters ) {
 
 	}
 
-	function updateDeferredNormalDepthUniforms( renderer, scene, camera, geometry, material, group ) {
+	function updateDeferredNormalDepthUniforms( renderer, scene, camera, geometry, material ) {
 
 		if ( ! _lightPrePass ) return;
 
@@ -512,7 +512,7 @@ THREE.WebGLDeferredRenderer = function ( parameters ) {
 
 	}
 
-	function updateDeferredColorUniforms( renderer, scene, camera, geometry, material, group ) {
+	function updateDeferredColorUniforms( renderer, scene, camera, geometry, material ) {
 
 		var originalMaterial = _originalMaterialsTable[ material.uuid ];
 		var uniforms = material.uniforms;
@@ -606,7 +606,7 @@ THREE.WebGLDeferredRenderer = function ( parameters ) {
 
 		}
 
-		updateDeferredColorUniforms( renderer, scene, camera, geometry, material, group );
+		updateDeferredColorUniforms( renderer, scene, camera, geometry, material );
 
 		material.uniforms.samplerLight.value = _compLight.renderTarget2.texture;
 
@@ -677,7 +677,7 @@ THREE.WebGLDeferredRenderer = function ( parameters ) {
 		var geometry = new THREE.PlaneBufferGeometry( 2, 2 );
 		var mesh = new THREE.Mesh( geometry, material );
 
-		mesh.onBeforeRender = function ( renderer, scene, camera, geometry, material, group ) {
+		mesh.onBeforeRender = function ( renderer, scene, camera, geometry, material ) {
 
 			material.uniforms.samplerColor.value = _compColor.renderTarget2.texture;
 
@@ -840,7 +840,7 @@ THREE.WebGLDeferredRenderer = function ( parameters ) {
 
 	}
 
-	function updateDeferredPointLightUniforms( renderer, scene, camera, geometry, material, group ) {
+	function updateDeferredPointLightUniforms( renderer, scene, camera, geometry, material ) {
 
 		var light = this;
 
@@ -886,7 +886,7 @@ THREE.WebGLDeferredRenderer = function ( parameters ) {
 
 	}
 
-	function updateDeferredSpotLightUniforms( renderer, scene, camera, geometry, material, group ) {
+	function updateDeferredSpotLightUniforms() {
 
 		var light = this;
 
@@ -929,7 +929,7 @@ THREE.WebGLDeferredRenderer = function ( parameters ) {
 
 	}
 
-	function updateDeferredDirectionalLightUniforms( renderer, scene, camera, geometry, material, group ) {
+	function updateDeferredDirectionalLightUniforms() {
 
 		var light = this;
 

+ 328 - 0
examples/js/shaders/GodRaysShader.js

@@ -0,0 +1,328 @@
+/**
+ * @author huwb / http://huwbowles.com/
+ *
+ * God-rays (crepuscular rays)
+ *
+ * Similar implementation to the one used by Crytek for CryEngine 2 [Sousa2008].
+ * Blurs a mask generated from the depth map along radial lines emanating from the light
+ * source. The blur repeatedly applies a blur filter of increasing support but constant
+ * sample count to produce a blur filter with large support.
+ *
+ * My implementation performs 3 passes, similar to the implementation from Sousa. I found
+ * just 6 samples per pass produced acceptible results. The blur is applied three times,
+ * with decreasing filter support. The result is equivalent to a single pass with
+ * 6*6*6 = 216 samples.
+ *
+ * References:
+ *
+ * Sousa2008 - Crysis Next Gen Effects, GDC2008, http://www.crytek.com/sites/default/files/GDC08_SousaT_CrysisEffects.ppt
+ */
+
+THREE.GodRaysDepthMaskShader = {
+
+	uniforms: {
+
+		tInput: {
+			value: null
+		}
+
+	},
+
+	vertexShader: [
+
+		"varying vec2 vUv;",
+
+		"void main() {",
+
+		" vUv = uv;",
+		" gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
+
+		"}"
+
+	].join( "\n" ),
+
+	fragmentShader: [
+
+		"varying vec2 vUv;",
+
+		"uniform sampler2D tInput;",
+
+		"void main() {",
+
+		"	gl_FragColor = vec4( 1.0 ) - texture2D( tInput, vUv );",
+
+
+		"}"
+
+	].join( "\n" )
+
+};
+
+
+/**
+ * The god-ray generation shader.
+ *
+ * First pass:
+ *
+ * The depth map is blurred along radial lines towards the "sun". The
+ * output is written to a temporary render target (I used a 1/4 sized
+ * target).
+ *
+ * Pass two & three:
+ *
+ * The results of the previous pass are re-blurred, each time with a
+ * decreased distance between samples.
+ */
+
+THREE.GodRaysGenerateShader = {
+
+	uniforms: {
+
+		tInput: {
+			value: null
+		},
+		fStepSize: {
+			value: 1.0
+		},
+		vSunPositionScreenSpace: {
+			value: new THREE.Vector2( 0.5, 0.5 )
+		}
+
+	},
+
+	vertexShader: [
+
+		"varying vec2 vUv;",
+
+		"void main() {",
+
+		" vUv = uv;",
+		" gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
+
+		"}"
+
+	].join( "\n" ),
+
+	fragmentShader: [
+
+		"#define TAPS_PER_PASS 6.0",
+
+		"varying vec2 vUv;",
+
+		"uniform sampler2D tInput;",
+
+		"uniform vec2 vSunPositionScreenSpace;",
+		"uniform float fStepSize;", // filter step size
+
+		"void main() {",
+
+		// delta from current pixel to "sun" position
+
+		"	vec2 delta = vSunPositionScreenSpace - vUv;",
+		"	float dist = length( delta );",
+
+		// Step vector (uv space)
+
+		"	vec2 stepv = fStepSize * delta / dist;",
+
+		// Number of iterations between pixel and sun
+
+		"	float iters = dist/fStepSize;",
+
+		"	vec2 uv = vUv.xy;",
+		"	float col = 0.0;",
+
+		// This breaks ANGLE in Chrome 22
+		//	- see http://code.google.com/p/chromium/issues/detail?id=153105
+
+		/*
+		// Unrolling didnt do much on my hardware (ATI Mobility Radeon 3450),
+		// so i've just left the loop
+
+		"for ( float i = 0.0; i < TAPS_PER_PASS; i += 1.0 ) {",
+
+			// Accumulate samples, making sure we dont walk past the light source.
+
+			// The check for uv.y < 1 would not be necessary with "border" UV wrap
+			// mode, with a black border color. I don't think this is currently
+			// exposed by three.js. As a result there might be artifacts when the
+			// sun is to the left, right or bottom of screen as these cases are
+			// not specifically handled.
+
+			"col += ( i <= iters && uv.y < 1.0 ? texture2D( tInput, uv ).r : 0.0 );",
+			"uv += stepv;",
+
+		"}",
+		*/
+
+		// Unrolling loop manually makes it work in ANGLE
+
+		"	if ( 0.0 <= iters && uv.y < 1.0 ) col += texture2D( tInput, uv ).r;",
+		"	uv += stepv;",
+
+		"	if ( 1.0 <= iters && uv.y < 1.0 ) col += texture2D( tInput, uv ).r;",
+		"	uv += stepv;",
+
+		"	if ( 2.0 <= iters && uv.y < 1.0 ) col += texture2D( tInput, uv ).r;",
+		"	uv += stepv;",
+
+		"	if ( 3.0 <= iters && uv.y < 1.0 ) col += texture2D( tInput, uv ).r;",
+		"	uv += stepv;",
+
+		"	if ( 4.0 <= iters && uv.y < 1.0 ) col += texture2D( tInput, uv ).r;",
+		"	uv += stepv;",
+
+		"	if ( 5.0 <= iters && uv.y < 1.0 ) col += texture2D( tInput, uv ).r;",
+		"	uv += stepv;",
+
+		// Should technically be dividing by 'iters', but 'TAPS_PER_PASS' smooths out
+		// objectionable artifacts, in particular near the sun position. The side
+		// effect is that the result is darker than it should be around the sun, as
+		// TAPS_PER_PASS is greater than the number of samples actually accumulated.
+		// When the result is inverted (in the shader 'godrays_combine', this produces
+		// a slight bright spot at the position of the sun, even when it is occluded.
+
+		"	gl_FragColor = vec4( col/TAPS_PER_PASS );",
+		"	gl_FragColor.a = 1.0;",
+
+		"}"
+
+	].join( "\n" )
+
+};
+
+/**
+ * Additively applies god rays from texture tGodRays to a background (tColors).
+ * fGodRayIntensity attenuates the god rays.
+ */
+
+THREE.GodRaysCombineShader = {
+
+	uniforms: {
+
+		tColors: {
+			value: null
+		},
+
+		tGodRays: {
+			value: null
+		},
+
+		fGodRayIntensity: {
+			value: 0.69
+		},
+
+		vSunPositionScreenSpace: {
+			value: new THREE.Vector2( 0.5, 0.5 )
+		}
+
+	},
+
+	vertexShader: [
+
+		"varying vec2 vUv;",
+
+		"void main() {",
+
+		"	vUv = uv;",
+		"	gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
+
+		"}"
+
+	].join( "\n" ),
+
+	fragmentShader: [
+
+		"varying vec2 vUv;",
+
+		"uniform sampler2D tColors;",
+		"uniform sampler2D tGodRays;",
+
+		"uniform vec2 vSunPositionScreenSpace;",
+		"uniform float fGodRayIntensity;",
+
+		"void main() {",
+
+		// Since THREE.MeshDepthMaterial renders foreground objects white and background
+		// objects black, the god-rays will be white streaks. Therefore value is inverted
+		// before being combined with tColors
+
+		"	gl_FragColor = texture2D( tColors, vUv ) + fGodRayIntensity * vec4( 1.0 - texture2D( tGodRays, vUv ).r );",
+		"	gl_FragColor.a = 1.0;",
+
+		"}"
+
+	].join( "\n" )
+
+};
+
+
+/**
+ * A dodgy sun/sky shader. Makes a bright spot at the sun location. Would be
+ * cheaper/faster/simpler to implement this as a simple sun sprite.
+ */
+
+THREE.GodRaysFakeSunShader = {
+
+	uniforms: {
+
+		vSunPositionScreenSpace: {
+			value: new THREE.Vector2( 0.5, 0.5 )
+		},
+
+		fAspect: {
+			value: 1.0
+		},
+
+		sunColor: {
+			value: new THREE.Color( 0xffee00 )
+		},
+
+		bgColor: {
+			value: new THREE.Color( 0x000000 )
+		}
+
+	},
+
+	vertexShader: [
+
+		"varying vec2 vUv;",
+
+		"void main() {",
+
+		"	vUv = uv;",
+		"	gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
+
+		"}"
+
+	].join( "\n" ),
+
+	fragmentShader: [
+
+		"varying vec2 vUv;",
+
+		"uniform vec2 vSunPositionScreenSpace;",
+		"uniform float fAspect;",
+
+		"uniform vec3 sunColor;",
+		"uniform vec3 bgColor;",
+
+		"void main() {",
+
+		"	vec2 diff = vUv - vSunPositionScreenSpace;",
+
+		// Correct for aspect ratio
+
+		"	diff.x *= fAspect;",
+
+		"	float prop = clamp( length( diff ) / 0.5, 0.0, 1.0 );",
+		"	prop = 0.35 * pow( 1.0 - prop, 3.0 );",
+
+		"	gl_FragColor.xyz = mix( sunColor, bgColor, 1.0 - prop );",
+		"	gl_FragColor.w = 1.0;",
+
+		"}"
+
+	].join( "\n" )
+
+};

+ 686 - 0
examples/js/shaders/SkinShader.js

@@ -0,0 +1,686 @@
+/**
+ * @author alteredq / http://alteredqualia.com/
+ *
+ */
+
+/* ------------------------------------------------------------------------------------------
+//	Basic skin shader
+//		- per-pixel Blinn-Phong diffuse term mixed with half-Lambert wrap-around term (per color component)
+//		- physically based specular term (Kelemen/Szirmay-Kalos specular reflectance)
+//
+//		- diffuse map
+//		- bump map
+//		- specular map
+//		- point, directional and hemisphere lights (use with "lights: true" material option)
+//		- fog (use with "fog: true" material option)
+//
+// ------------------------------------------------------------------------------------------ */
+
+THREE.SkinShaderBasic = {
+
+	uniforms: THREE.UniformsUtils.merge( [
+
+		THREE.UniformsLib[ "fog" ],
+		THREE.UniformsLib[ "lights" ],
+
+		{
+
+			"enableBump": { value: 0 },
+			"enableSpecular": { value: 0 },
+
+			"tDiffuse": { value: null },
+			"tBeckmann": { value: null },
+
+			"diffuse": { value: new THREE.Color( 0xeeeeee ) },
+			"specular": { value: new THREE.Color( 0x111111 ) },
+			"opacity": { value: 1 },
+
+			"uRoughness": { value: 0.15 },
+			"uSpecularBrightness": { value: 0.75 },
+
+			"bumpMap": { value: null },
+			"bumpScale": { value: 1 },
+
+			"specularMap": { value: null },
+
+			"offsetRepeat": { value: new THREE.Vector4( 0, 0, 1, 1 ) },
+
+			"uWrapRGB": { value: new THREE.Vector3( 0.75, 0.375, 0.1875 ) }
+
+		}
+
+	] ),
+
+	vertexShader: [
+
+		"uniform vec4 offsetRepeat;",
+
+		"varying vec3 vNormal;",
+		"varying vec2 vUv;",
+
+		"varying vec3 vViewPosition;",
+
+		THREE.ShaderChunk[ "common" ],
+		THREE.ShaderChunk[ "lights_pars_begin" ],
+		THREE.ShaderChunk[ "fog_pars_vertex" ],
+
+		"void main() {",
+
+		"	vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );",
+		"	vec4 worldPosition = modelMatrix * vec4( position, 1.0 );",
+
+		"	vViewPosition = -mvPosition.xyz;",
+
+		"	vNormal = normalize( normalMatrix * normal );",
+
+		"	vUv = uv * offsetRepeat.zw + offsetRepeat.xy;",
+
+		"	gl_Position = projectionMatrix * mvPosition;",
+
+		THREE.ShaderChunk[ "fog_vertex" ],
+
+		"}"
+
+	].join( "\n" ),
+
+	fragmentShader: [
+
+		"#define USE_BUMPMAP",
+
+		"uniform bool enableBump;",
+		"uniform bool enableSpecular;",
+
+		"uniform vec3 diffuse;",
+		"uniform vec3 specular;",
+		"uniform float opacity;",
+
+		"uniform float uRoughness;",
+		"uniform float uSpecularBrightness;",
+
+		"uniform vec3 uWrapRGB;",
+
+		"uniform sampler2D tDiffuse;",
+		"uniform sampler2D tBeckmann;",
+
+		"uniform sampler2D specularMap;",
+
+		"varying vec3 vNormal;",
+		"varying vec2 vUv;",
+
+		"varying vec3 vViewPosition;",
+
+		THREE.ShaderChunk[ "common" ],
+		THREE.ShaderChunk[ "bsdfs" ],
+		THREE.ShaderChunk[ "packing" ],
+		THREE.ShaderChunk[ "lights_pars_begin" ],
+		THREE.ShaderChunk[ "fog_pars_fragment" ],
+		THREE.ShaderChunk[ "bumpmap_pars_fragment" ],
+
+		// Fresnel term
+
+		"float fresnelReflectance( vec3 H, vec3 V, float F0 ) {",
+
+		"	float base = 1.0 - dot( V, H );",
+		"	float exponential = pow( base, 5.0 );",
+
+		"	return exponential + F0 * ( 1.0 - exponential );",
+
+		"}",
+
+		// Kelemen/Szirmay-Kalos specular BRDF
+
+		"float KS_Skin_Specular( vec3 N,", // Bumped surface normal
+		"	vec3 L,", // Points to light
+		"	vec3 V,", // Points to eye
+		"	float m,", // Roughness
+		"	float rho_s", // Specular brightness
+		"	) {",
+
+		"	float result = 0.0;",
+		"	float ndotl = dot( N, L );",
+
+		"	if( ndotl > 0.0 ) {",
+
+		"		vec3 h = L + V;", // Unnormalized half-way vector
+		"		vec3 H = normalize( h );",
+
+		"		float ndoth = dot( N, H );",
+
+		"		float PH = pow( 2.0 * texture2D( tBeckmann, vec2( ndoth, m ) ).x, 10.0 );",
+
+		"		float F = fresnelReflectance( H, V, 0.028 );",
+		"		float frSpec = max( PH * F / dot( h, h ), 0.0 );",
+
+		"		result = ndotl * rho_s * frSpec;", // BRDF * dot(N,L) * rho_s
+
+		"	}",
+
+		"	return result;",
+
+		"}",
+
+		"void main() {",
+
+		"	vec3 outgoingLight = vec3( 0.0 );",	// outgoing light does not have an alpha, the surface does
+		"	vec4 diffuseColor = vec4( diffuse, opacity );",
+
+		"	vec4 colDiffuse = texture2D( tDiffuse, vUv );",
+		"	colDiffuse.rgb *= colDiffuse.rgb;",
+
+		"	diffuseColor = diffuseColor * colDiffuse;",
+
+		"	vec3 normal = normalize( vNormal );",
+		"	vec3 viewerDirection = normalize( vViewPosition );",
+
+		"	float specularStrength;",
+
+		"	if ( enableSpecular ) {",
+
+		"		vec4 texelSpecular = texture2D( specularMap, vUv );",
+		"		specularStrength = texelSpecular.r;",
+
+		"	} else {",
+
+		"		specularStrength = 1.0;",
+
+		"	}",
+
+		"	#ifdef USE_BUMPMAP",
+
+		"		if ( enableBump ) normal = perturbNormalArb( -vViewPosition, normal, dHdxy_fwd() );",
+
+		"	#endif",
+
+		// point lights
+
+		"	vec3 totalSpecularLight = vec3( 0.0 );",
+		"	vec3 totalDiffuseLight = vec3( 0.0 );",
+
+		"	#if NUM_POINT_LIGHTS > 0",
+
+		"		for ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {",
+
+		"			vec3 lVector = pointLights[ i ].position + vViewPosition.xyz;",
+
+		"			float attenuation = calcLightAttenuation( length( lVector ), pointLights[ i ].distance, pointLights[ i ].decay );",
+
+		"			lVector = normalize( lVector );",
+
+		"			float pointDiffuseWeightFull = max( dot( normal, lVector ), 0.0 );",
+		"			float pointDiffuseWeightHalf = max( 0.5 * dot( normal, lVector ) + 0.5, 0.0 );",
+		"			vec3 pointDiffuseWeight = mix( vec3 ( pointDiffuseWeightFull ), vec3( pointDiffuseWeightHalf ), uWrapRGB );",
+
+		"			float pointSpecularWeight = KS_Skin_Specular( normal, lVector, viewerDirection, uRoughness, uSpecularBrightness );",
+
+		"			totalDiffuseLight += pointLight[ i ].color * ( pointDiffuseWeight * attenuation );",
+		"			totalSpecularLight += pointLight[ i ].color * specular * ( pointSpecularWeight * specularStrength * attenuation );",
+
+		"		}",
+
+		"	#endif",
+
+		// directional lights
+
+		"	#if NUM_DIR_LIGHTS > 0",
+
+		"		for( int i = 0; i < NUM_DIR_LIGHTS; i++ ) {",
+
+		"			vec3 dirVector = directionalLights[ i ].direction;",
+
+		"			float dirDiffuseWeightFull = max( dot( normal, dirVector ), 0.0 );",
+		"			float dirDiffuseWeightHalf = max( 0.5 * dot( normal, dirVector ) + 0.5, 0.0 );",
+		"			vec3 dirDiffuseWeight = mix( vec3 ( dirDiffuseWeightFull ), vec3( dirDiffuseWeightHalf ), uWrapRGB );",
+
+		"			float dirSpecularWeight = KS_Skin_Specular( normal, dirVector, viewerDirection, uRoughness, uSpecularBrightness );",
+
+		"			totalDiffuseLight += directionalLights[ i ].color * dirDiffuseWeight;",
+		"			totalSpecularLight += directionalLights[ i ].color * ( dirSpecularWeight * specularStrength );",
+
+		"		}",
+
+		"	#endif",
+
+		// hemisphere lights
+
+		"	#if NUM_HEMI_LIGHTS > 0",
+
+		"		for ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) {",
+
+		"			vec3 lVector = hemisphereLightDirection[ i ];",
+
+		"			float dotProduct = dot( normal, lVector );",
+		"			float hemiDiffuseWeight = 0.5 * dotProduct + 0.5;",
+
+		"			totalDiffuseLight += mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeight );",
+
+		// specular (sky light)
+
+		"			float hemiSpecularWeight = 0.0;",
+		"			hemiSpecularWeight += KS_Skin_Specular( normal, lVector, viewerDirection, uRoughness, uSpecularBrightness );",
+
+		// specular (ground light)
+
+		"			vec3 lVectorGround = -lVector;",
+		"			hemiSpecularWeight += KS_Skin_Specular( normal, lVectorGround, viewerDirection, uRoughness, uSpecularBrightness );",
+
+		"			vec3 hemiSpecularColor = mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeight );",
+
+		"			totalSpecularLight += hemiSpecularColor * specular * ( hemiSpecularWeight * specularStrength );",
+
+		"		}",
+
+		"	#endif",
+
+		"	outgoingLight += diffuseColor.xyz * ( totalDiffuseLight + ambientLightColor * diffuse ) + totalSpecularLight;",
+
+		"	gl_FragColor = linearToOutputTexel( vec4( outgoingLight, diffuseColor.a ) );",	// TODO, this should be pre-multiplied to allow for bright highlights on very transparent objects
+
+		THREE.ShaderChunk[ "fog_fragment" ],
+
+		"}"
+
+	].join( "\n" )
+
+};
+
+/* ------------------------------------------------------------------------------------------
+//	Skin shader
+//		- Blinn-Phong diffuse term (using normal + diffuse maps)
+//		- subsurface scattering approximation by four blur layers
+//		- physically based specular term (Kelemen/Szirmay-Kalos specular reflectance)
+//
+//		- point and directional lights (use with "lights: true" material option)
+//
+//		- based on Nvidia Advanced Skin Rendering GDC 2007 presentation
+//		  and GPU Gems 3 Chapter 14. Advanced Techniques for Realistic Real-Time Skin Rendering
+//
+//			http://developer.download.nvidia.com/presentations/2007/gdc/Advanced_Skin.pdf
+//			http://http.developer.nvidia.com/GPUGems3/gpugems3_ch14.html
+// ------------------------------------------------------------------------------------------ */
+
+THREE.SkinShaderAdvanced = {
+
+	uniforms: THREE.UniformsUtils.merge( [
+
+		THREE.UniformsLib[ "fog" ],
+		THREE.UniformsLib[ "lights" ],
+
+		{
+			"passID": { value: 0 },
+
+			"tDiffuse": { value: null },
+			"tNormal": { value: null },
+
+			"tBlur1": { value: null },
+			"tBlur2": { value: null },
+			"tBlur3": { value: null },
+			"tBlur4": { value: null },
+
+			"tBeckmann": { value: null },
+
+			"uNormalScale": { value: 1.0 },
+
+			"diffuse": { value: new THREE.Color( 0xeeeeee ) },
+			"specular": { value: new THREE.Color( 0x111111 ) },
+			"opacity": { value: 1 },
+
+			"uRoughness": { value: 0.15 },
+			"uSpecularBrightness": { value: 0.75 }
+
+		}
+
+	] ),
+
+	vertexShader: [
+
+		"#ifdef VERTEX_TEXTURES",
+
+		"	uniform sampler2D tDisplacement;",
+		"	uniform float uDisplacementScale;",
+		"	uniform float uDisplacementBias;",
+
+		"#endif",
+
+		"varying vec3 vNormal;",
+		"varying vec2 vUv;",
+
+		"varying vec3 vViewPosition;",
+
+		THREE.ShaderChunk[ "common" ],
+		THREE.ShaderChunk[ "fog_pars_vertex" ],
+
+		"void main() {",
+
+		"	vec4 worldPosition = modelMatrix * vec4( position, 1.0 );",
+
+		"	vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );",
+
+		"	vViewPosition = -mvPosition.xyz;",
+
+		"	vNormal = normalize( normalMatrix * normal );",
+
+		"	vUv = uv;",
+
+		// displacement mapping
+
+		"	#ifdef VERTEX_TEXTURES",
+
+		"		vec3 dv = texture2D( tDisplacement, uv ).xyz;",
+		"		float df = uDisplacementScale * dv.x + uDisplacementBias;",
+		"		vec4 displacedPosition = vec4( vNormal.xyz * df, 0.0 ) + mvPosition;",
+		"		gl_Position = projectionMatrix * displacedPosition;",
+
+		"		#else",
+
+		"		gl_Position = projectionMatrix * mvPosition;",
+
+		"	#endif",
+
+		THREE.ShaderChunk[ "fog_vertex" ],
+
+		"}",
+
+
+	].join( "\n" ),
+
+	vertexShaderUV: [
+
+		"varying vec3 vNormal;",
+		"varying vec2 vUv;",
+
+		"varying vec3 vViewPosition;",
+
+		THREE.ShaderChunk[ "common" ],
+
+		"void main() {",
+
+		"	vec4 worldPosition = modelMatrix * vec4( position, 1.0 );",
+
+		"	vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );",
+
+		"	vViewPosition = -mvPosition.xyz;",
+
+		"	vNormal = normalize( normalMatrix * normal );",
+
+		"	vUv = uv;",
+
+		"	gl_Position = vec4( uv.x * 2.0 - 1.0, uv.y * 2.0 - 1.0, 0.0, 1.0 );",
+
+		"}"
+
+	].join( "\n" ),
+
+	fragmentShader: [
+
+		"uniform vec3 diffuse;",
+		"uniform vec3 specular;",
+		"uniform float opacity;",
+
+		"uniform float uRoughness;",
+		"uniform float uSpecularBrightness;",
+
+		"uniform int passID;",
+
+		"uniform sampler2D tDiffuse;",
+		"uniform sampler2D tNormal;",
+
+		"uniform sampler2D tBlur1;",
+		"uniform sampler2D tBlur2;",
+		"uniform sampler2D tBlur3;",
+		"uniform sampler2D tBlur4;",
+
+		"uniform sampler2D tBeckmann;",
+
+		"uniform float uNormalScale;",
+
+		"varying vec3 vNormal;",
+		"varying vec2 vUv;",
+
+		"varying vec3 vViewPosition;",
+
+		THREE.ShaderChunk[ "common" ],
+		THREE.ShaderChunk[ "lights_pars_begin" ],
+		THREE.ShaderChunk[ "fog_pars_fragment" ],
+
+		"float fresnelReflectance( vec3 H, vec3 V, float F0 ) {",
+
+		"	float base = 1.0 - dot( V, H );",
+		"	float exponential = pow( base, 5.0 );",
+
+		"	return exponential + F0 * ( 1.0 - exponential );",
+
+		"}",
+
+		// Kelemen/Szirmay-Kalos specular BRDF
+
+		"float KS_Skin_Specular( vec3 N,", // Bumped surface normal
+		"	vec3 L,", // Points to light
+		"	vec3 V,", // Points to eye
+		"	float m,", // Roughness
+		"	float rho_s", // Specular brightness
+		"	) {",
+
+		"	float result = 0.0;",
+		"	float ndotl = dot( N, L );",
+
+		"	if( ndotl > 0.0 ) {",
+
+		"		vec3 h = L + V;", // Unnormalized half-way vector
+		"		vec3 H = normalize( h );",
+
+		"		float ndoth = dot( N, H );",
+
+		"		float PH = pow( 2.0 * texture2D( tBeckmann, vec2( ndoth, m ) ).x, 10.0 );",
+		"		float F = fresnelReflectance( H, V, 0.028 );",
+		"		float frSpec = max( PH * F / dot( h, h ), 0.0 );",
+
+		"		result = ndotl * rho_s * frSpec;", // BRDF * dot(N,L) * rho_s
+
+		"	}",
+
+		"	return result;",
+
+		"}",
+
+		"void main() {",
+
+		"	vec3 outgoingLight = vec3( 0.0 );",	// outgoing light does not have an alpha, the surface does
+		"	vec4 diffuseColor = vec4( diffuse, opacity );",
+
+		"	vec4 mSpecular = vec4( specular, opacity );",
+
+		"	vec4 colDiffuse = texture2D( tDiffuse, vUv );",
+		"	colDiffuse *= colDiffuse;",
+
+		"	diffuseColor *= colDiffuse;",
+
+		// normal mapping
+
+		"	vec4 posAndU = vec4( -vViewPosition, vUv.x );",
+		"	vec4 posAndU_dx = dFdx( posAndU ),  posAndU_dy = dFdy( posAndU );",
+		"	vec3 tangent = posAndU_dx.w * posAndU_dx.xyz + posAndU_dy.w * posAndU_dy.xyz;",
+		"	vec3 normal = normalize( vNormal );",
+		"	vec3 binormal = normalize( cross( tangent, normal ) );",
+		"	tangent = cross( normal, binormal );",	// no normalization required
+		"	mat3 tsb = mat3( tangent, binormal, normal );",
+
+		"	vec3 normalTex = texture2D( tNormal, vUv ).xyz * 2.0 - 1.0;",
+		"	normalTex.xy *= uNormalScale;",
+		"	normalTex = normalize( normalTex );",
+
+		"	vec3 finalNormal = tsb * normalTex;",
+		"	normal = normalize( finalNormal );",
+
+		"	vec3 viewerDirection = normalize( vViewPosition );",
+
+		// point lights
+
+		"	vec3 totalDiffuseLight = vec3( 0.0 );",
+		"	vec3 totalSpecularLight = vec3( 0.0 );",
+
+		"	#if NUM_POINT_LIGHTS > 0",
+
+		"	for ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {",
+
+		"		vec3 pointVector = normalize( pointLights[ i ].direction );",
+		"		float attenuation = calcLightAttenuation( length( lVector ), pointLights[ i ].distance, pointLights[ i ].decay );",
+
+		"		float pointDiffuseWeight = max( dot( normal, pointVector ), 0.0 );",
+
+		"		totalDiffuseLight += pointLightColor[ i ] * ( pointDiffuseWeight * attenuation );",
+
+		"		if ( passID == 1 ) {",
+
+		"			float pointSpecularWeight = KS_Skin_Specular( normal, pointVector, viewerDirection, uRoughness, uSpecularBrightness );",
+
+		"			totalSpecularLight += pointLightColor[ i ] * mSpecular.xyz * ( pointSpecularWeight * attenuation );",
+
+		"		}",
+
+		"	}",
+
+		"	#endif",
+
+		// directional lights
+
+		"	#if NUM_DIR_LIGHTS > 0",
+
+		"		for( int i = 0; i < NUM_DIR_LIGHTS; i++ ) {",
+
+		"			vec3 dirVector = directionalLights[ i ].direction;",
+
+		"			float dirDiffuseWeight = max( dot( normal, dirVector ), 0.0 );",
+
+
+		"			totalDiffuseLight += directionalLights[ i ].color * dirDiffuseWeight;",
+
+		"			if ( passID == 1 ) {",
+
+		"				float dirSpecularWeight = KS_Skin_Specular( normal, dirVector, viewerDirection, uRoughness, uSpecularBrightness );",
+
+		"				totalSpecularLight += directionalLights[ i ].color * mSpecular.xyz * dirSpecularWeight;",
+
+		"			}",
+
+		"		}",
+
+		"	#endif",
+
+		"	outgoingLight += diffuseColor.rgb * ( totalDiffuseLight + totalSpecularLight );",
+
+		"	if ( passID == 0 ) {",
+
+		"		outgoingLight = sqrt( outgoingLight );",
+
+		"	} else if ( passID == 1 ) {",
+
+		//"#define VERSION1",
+
+		"	#ifdef VERSION1",
+
+		"		vec3 nonblurColor = sqrt(outgoingLight );",
+
+		"	#else",
+
+		"		vec3 nonblurColor = outgoingLight;",
+
+		"	#endif",
+
+		"	vec3 blur1Color = texture2D( tBlur1, vUv ).xyz;",
+		"	vec3 blur2Color = texture2D( tBlur2, vUv ).xyz;",
+		"	vec3 blur3Color = texture2D( tBlur3, vUv ).xyz;",
+		"	vec3 blur4Color = texture2D( tBlur4, vUv ).xyz;",
+
+
+		//"gl_FragColor = vec4( blur1Color, gl_FragColor.w );",
+
+		//"gl_FragColor = vec4( vec3( 0.22, 0.5, 0.7 ) * nonblurColor + vec3( 0.2, 0.5, 0.3 ) * blur1Color + vec3( 0.58, 0.0, 0.0 ) * blur2Color, gl_FragColor.w );",
+
+		//"gl_FragColor = vec4( vec3( 0.25, 0.6, 0.8 ) * nonblurColor + vec3( 0.15, 0.25, 0.2 ) * blur1Color + vec3( 0.15, 0.15, 0.0 ) * blur2Color + vec3( 0.45, 0.0, 0.0 ) * blur3Color, gl_FragColor.w );",
+
+		"	outgoingLight = vec3( vec3( 0.22,  0.437, 0.635 ) * nonblurColor + ",
+		"		vec3( 0.101, 0.355, 0.365 ) * blur1Color + ",
+		"		vec3( 0.119, 0.208, 0.0 )   * blur2Color + ",
+		"		vec3( 0.114, 0.0,   0.0 )   * blur3Color + ",
+		"		vec3( 0.444, 0.0,   0.0 )   * blur4Color );",
+
+		"	outgoingLight *= sqrt( colDiffuse.xyz );",
+
+		"	outgoingLight += ambientLightColor * diffuse * colDiffuse.xyz + totalSpecularLight;",
+
+		"		#ifndef VERSION1",
+
+		"			outgoingLight = sqrt( outgoingLight );",
+
+		"		#endif",
+
+		"	}",
+
+		"	gl_FragColor = vec4( outgoingLight, diffuseColor.a );",	// TODO, this should be pre-multiplied to allow for bright highlights on very transparent objects
+
+		THREE.ShaderChunk[ "fog_fragment" ],
+
+		"}"
+
+	].join( "\n" )
+
+};
+
+/* ------------------------------------------------------------------------------------------
+// Beckmann distribution function
+//	- to be used in specular term of skin shader
+//	- render a screen-aligned quad to precompute a 512 x 512 texture
+//
+//		- from http://developer.nvidia.com/node/171
+ ------------------------------------------------------------------------------------------ */
+
+THREE.SkinShaderBeckmann = {
+
+	uniforms: {},
+
+	vertexShader: [
+
+		"varying vec2 vUv;",
+
+		"void main() {",
+
+		"	vUv = uv;",
+		"	gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
+
+		"}"
+
+	].join( "\n" ),
+
+	fragmentShader: [
+
+		"varying vec2 vUv;",
+
+		"float PHBeckmann( float ndoth, float m ) {",
+
+		"	float alpha = acos( ndoth );",
+		"	float ta = tan( alpha );",
+
+		"	float val = 1.0 / ( m * m * pow( ndoth, 4.0 ) ) * exp( -( ta * ta ) / ( m * m ) );",
+		"	return val;",
+
+		"}",
+
+		"float KSTextureCompute( vec2 tex ) {",
+
+		// Scale the value to fit within [0,1]  invert upon lookup.
+
+		"	return 0.5 * pow( PHBeckmann( tex.x, tex.y ), 0.1 );",
+
+		"}",
+
+		"void main() {",
+
+		"	float x = KSTextureCompute( vUv );",
+
+		"	gl_FragColor = vec4( x, x, x, 1.0 );",
+
+		"}"
+
+	].join( "\n" )
+
+};

+ 320 - 0
examples/js/shaders/TerrainShader.js

@@ -0,0 +1,320 @@
+/**
+ * @author alteredq / http://alteredqualia.com/
+ *
+ */
+
+THREE.TerrainShader = {
+
+	/* -------------------------------------------------------------------------
+	//	Dynamic terrain shader
+	//		- Blinn-Phong
+	//		- height + normal + diffuse1 + diffuse2 + specular + detail maps
+	//		- point, directional and hemisphere lights (use with "lights: true" material option)
+	//		- shadow maps receiving
+	 ------------------------------------------------------------------------- */
+
+	uniforms: THREE.UniformsUtils.merge( [
+
+		THREE.UniformsLib[ "fog" ],
+		THREE.UniformsLib[ "lights" ],
+
+		{
+
+			"enableDiffuse1": { value: 0 },
+			"enableDiffuse2": { value: 0 },
+			"enableSpecular": { value: 0 },
+			"enableReflection": { value: 0 },
+
+			"tDiffuse1": { value: null },
+			"tDiffuse2": { value: null },
+			"tDetail": { value: null },
+			"tNormal": { value: null },
+			"tSpecular": { value: null },
+			"tDisplacement": { value: null },
+
+			"uNormalScale": { value: 1.0 },
+
+			"uDisplacementBias": { value: 0.0 },
+			"uDisplacementScale": { value: 1.0 },
+
+			"diffuse": { value: new THREE.Color( 0xeeeeee ) },
+			"specular": { value: new THREE.Color( 0x111111 ) },
+			"shininess": { value: 30 },
+			"opacity": { value: 1 },
+
+			"uRepeatBase": { value: new THREE.Vector2( 1, 1 ) },
+			"uRepeatOverlay": { value: new THREE.Vector2( 1, 1 ) },
+
+			"uOffset": { value: new THREE.Vector2( 0, 0 ) }
+
+		}
+
+	] ),
+
+	fragmentShader: [
+
+		"uniform vec3 diffuse;",
+		"uniform vec3 specular;",
+		"uniform float shininess;",
+		"uniform float opacity;",
+
+		"uniform bool enableDiffuse1;",
+		"uniform bool enableDiffuse2;",
+		"uniform bool enableSpecular;",
+
+		"uniform sampler2D tDiffuse1;",
+		"uniform sampler2D tDiffuse2;",
+		"uniform sampler2D tDetail;",
+		"uniform sampler2D tNormal;",
+		"uniform sampler2D tSpecular;",
+		"uniform sampler2D tDisplacement;",
+
+		"uniform float uNormalScale;",
+
+		"uniform vec2 uRepeatOverlay;",
+		"uniform vec2 uRepeatBase;",
+
+		"uniform vec2 uOffset;",
+
+		"varying vec3 vTangent;",
+		"varying vec3 vBinormal;",
+		"varying vec3 vNormal;",
+		"varying vec2 vUv;",
+
+		"varying vec3 vViewPosition;",
+
+		THREE.ShaderChunk[ "common" ],
+		THREE.ShaderChunk[ "bsdfs" ],
+		THREE.ShaderChunk[ "lights_pars_begin" ],
+		THREE.ShaderChunk[ "shadowmap_pars_fragment" ],
+		THREE.ShaderChunk[ "fog_pars_fragment" ],
+
+		"float calcLightAttenuation( float lightDistance, float cutoffDistance, float decayExponent ) {",
+				"if ( decayExponent > 0.0 ) {",
+					"return pow( saturate( - lightDistance / cutoffDistance + 1.0 ), decayExponent );",
+				"}",
+				"return 1.0;",
+			"}",
+
+		"void main() {",
+
+			"vec3 outgoingLight = vec3( 0.0 );",	// outgoing light does not have an alpha, the surface does
+			"vec4 diffuseColor = vec4( diffuse, opacity );",
+
+			"vec3 specularTex = vec3( 1.0 );",
+
+			"vec2 uvOverlay = uRepeatOverlay * vUv + uOffset;",
+			"vec2 uvBase = uRepeatBase * vUv;",
+
+			"vec3 normalTex = texture2D( tDetail, uvOverlay ).xyz * 2.0 - 1.0;",
+			"normalTex.xy *= uNormalScale;",
+			"normalTex = normalize( normalTex );",
+
+			"if( enableDiffuse1 && enableDiffuse2 ) {",
+
+				"vec4 colDiffuse1 = texture2D( tDiffuse1, uvOverlay );",
+				"vec4 colDiffuse2 = texture2D( tDiffuse2, uvOverlay );",
+
+				"colDiffuse1 = GammaToLinear( colDiffuse1, float( GAMMA_FACTOR ) );",
+				"colDiffuse2 = GammaToLinear( colDiffuse2, float( GAMMA_FACTOR ) );",
+
+				"diffuseColor *= mix ( colDiffuse1, colDiffuse2, 1.0 - texture2D( tDisplacement, uvBase ) );",
+
+			" } else if( enableDiffuse1 ) {",
+
+				"diffuseColor *= texture2D( tDiffuse1, uvOverlay );",
+
+			"} else if( enableDiffuse2 ) {",
+
+				"diffuseColor *= texture2D( tDiffuse2, uvOverlay );",
+
+			"}",
+
+			"if( enableSpecular )",
+				"specularTex = texture2D( tSpecular, uvOverlay ).xyz;",
+
+			"mat3 tsb = mat3( vTangent, vBinormal, vNormal );",
+			"vec3 finalNormal = tsb * normalTex;",
+
+			"vec3 normal = normalize( finalNormal );",
+			"vec3 viewPosition = normalize( vViewPosition );",
+
+			"vec3 totalDiffuseLight = vec3( 0.0 );",
+			"vec3 totalSpecularLight = vec3( 0.0 );",
+
+			// point lights
+
+			"#if NUM_POINT_LIGHTS > 0",
+
+				"for ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {",
+
+					"vec3 lVector = pointLights[ i ].position + vViewPosition.xyz;",
+
+					"float attenuation = calcLightAttenuation( length( lVector ), pointLights[ i ].distance, pointLights[ i ].decay );",
+
+					"lVector = normalize( lVector );",
+
+					"vec3 pointHalfVector = normalize( lVector + viewPosition );",
+
+					"float pointDotNormalHalf = max( dot( normal, pointHalfVector ), 0.0 );",
+					"float pointDiffuseWeight = max( dot( normal, lVector ), 0.0 );",
+
+					"float pointSpecularWeight = specularTex.r * max( pow( pointDotNormalHalf, shininess ), 0.0 );",
+
+					"totalDiffuseLight += attenuation * pointLights[ i ].color * pointDiffuseWeight;",
+					"totalSpecularLight += attenuation * pointLights[ i ].color * specular * pointSpecularWeight * pointDiffuseWeight;",
+
+				"}",
+
+			"#endif",
+
+			// directional lights
+
+			"#if NUM_DIR_LIGHTS > 0",
+
+				"vec3 dirDiffuse = vec3( 0.0 );",
+				"vec3 dirSpecular = vec3( 0.0 );",
+
+				"for( int i = 0; i < NUM_DIR_LIGHTS; i++ ) {",
+
+					"vec3 dirVector = directionalLights[ i ].direction;",
+					"vec3 dirHalfVector = normalize( dirVector + viewPosition );",
+
+					"float dirDotNormalHalf = max( dot( normal, dirHalfVector ), 0.0 );",
+					"float dirDiffuseWeight = max( dot( normal, dirVector ), 0.0 );",
+
+					"float dirSpecularWeight = specularTex.r * max( pow( dirDotNormalHalf, shininess ), 0.0 );",
+
+					"totalDiffuseLight += directionalLights[ i ].color * dirDiffuseWeight;",
+					"totalSpecularLight += directionalLights[ i ].color * specular * dirSpecularWeight * dirDiffuseWeight;",
+
+				"}",
+
+			"#endif",
+
+			// hemisphere lights
+
+			"#if NUM_HEMI_LIGHTS > 0",
+
+				"vec3 hemiDiffuse  = vec3( 0.0 );",
+				"vec3 hemiSpecular = vec3( 0.0 );",
+
+				"for( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) {",
+
+					"vec3 lVector = hemisphereLightDirection[ i ];",
+
+					// diffuse
+
+					"float dotProduct = dot( normal, lVector );",
+					"float hemiDiffuseWeight = 0.5 * dotProduct + 0.5;",
+
+					"totalDiffuseLight += mix( hemisphereLights[ i ].groundColor, hemisphereLights[ i ].skyColor, hemiDiffuseWeight );",
+
+					// specular (sky light)
+
+					"float hemiSpecularWeight = 0.0;",
+
+					"vec3 hemiHalfVectorSky = normalize( lVector + viewPosition );",
+					"float hemiDotNormalHalfSky = 0.5 * dot( normal, hemiHalfVectorSky ) + 0.5;",
+					"hemiSpecularWeight += specularTex.r * max( pow( hemiDotNormalHalfSky, shininess ), 0.0 );",
+
+					// specular (ground light)
+
+					"vec3 lVectorGround = -lVector;",
+
+					"vec3 hemiHalfVectorGround = normalize( lVectorGround + viewPosition );",
+					"float hemiDotNormalHalfGround = 0.5 * dot( normal, hemiHalfVectorGround ) + 0.5;",
+					"hemiSpecularWeight += specularTex.r * max( pow( hemiDotNormalHalfGround, shininess ), 0.0 );",
+
+					"totalSpecularLight += specular * mix( hemisphereLights[ i ].groundColor, hemisphereLights[ i ].skyColor, hemiDiffuseWeight ) * hemiSpecularWeight * hemiDiffuseWeight;",
+
+				"}",
+
+			"#endif",
+
+			"outgoingLight += diffuseColor.xyz * ( totalDiffuseLight + ambientLightColor + totalSpecularLight );",
+
+			"gl_FragColor = vec4( outgoingLight, diffuseColor.a );",	// TODO, this should be pre-multiplied to allow for bright highlights on very transparent objects
+
+			THREE.ShaderChunk[ "fog_fragment" ],
+
+		"}"
+
+	].join( "\n" ),
+
+	vertexShader: [
+
+		"attribute vec4 tangent;",
+
+		"uniform vec2 uRepeatBase;",
+
+		"uniform sampler2D tNormal;",
+
+		"#ifdef VERTEX_TEXTURES",
+
+			"uniform sampler2D tDisplacement;",
+			"uniform float uDisplacementScale;",
+			"uniform float uDisplacementBias;",
+
+		"#endif",
+
+		"varying vec3 vTangent;",
+		"varying vec3 vBinormal;",
+		"varying vec3 vNormal;",
+		"varying vec2 vUv;",
+
+		"varying vec3 vViewPosition;",
+
+		THREE.ShaderChunk[ "shadowmap_pars_vertex" ],
+		THREE.ShaderChunk[ "fog_pars_vertex" ],
+
+		"void main() {",
+
+			"vNormal = normalize( normalMatrix * normal );",
+
+			// tangent and binormal vectors
+
+			"vTangent = normalize( normalMatrix * tangent.xyz );",
+
+			"vBinormal = cross( vNormal, vTangent ) * tangent.w;",
+			"vBinormal = normalize( vBinormal );",
+
+			// texture coordinates
+
+			"vUv = uv;",
+
+			"vec2 uvBase = uv * uRepeatBase;",
+
+			// displacement mapping
+
+			"#ifdef VERTEX_TEXTURES",
+
+				"vec3 dv = texture2D( tDisplacement, uvBase ).xyz;",
+				"float df = uDisplacementScale * dv.x + uDisplacementBias;",
+				"vec3 displacedPosition = normal * df + position;",
+
+				"vec4 worldPosition = modelMatrix * vec4( displacedPosition, 1.0 );",
+				"vec4 mvPosition = modelViewMatrix * vec4( displacedPosition, 1.0 );",
+
+			"#else",
+
+				"vec4 worldPosition = modelMatrix * vec4( position, 1.0 );",
+				"vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );",
+
+			"#endif",
+
+			"gl_Position = projectionMatrix * mvPosition;",
+
+			"vViewPosition = -mvPosition.xyz;",
+
+			"vec3 normalTex = texture2D( tNormal, uvBase ).xyz * 2.0 - 1.0;",
+			"vNormal = normalMatrix * normalTex;",
+
+			THREE.ShaderChunk[ "shadowmap_vertex" ],
+			THREE.ShaderChunk[ "fog_vertex" ],
+
+		"}"
+
+	].join( "\n" )
+
+};

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