Mr.doob 7 years ago
parent
commit
3d2bc3c853
100 changed files with 5730 additions and 4644 deletions
  1. 93 56
      build/three.js
  2. 348 396
      build/three.min.js
  3. 93 56
      build/three.module.js
  4. 8 3
      docs/api/animation/AnimationClip.html
  5. 3 4
      docs/api/animation/AnimationMixer.html
  6. 11 9
      docs/api/animation/KeyframeTrack.html
  7. 5 5
      docs/api/animation/PropertyMixer.html
  8. 1 1
      docs/api/cameras/Camera.html
  9. 1 1
      docs/api/cameras/StereoCamera.html
  10. 15 15
      docs/api/core/BufferAttribute.html
  11. 2 2
      docs/api/core/DirectGeometry.html
  12. 3 3
      docs/api/core/InterleavedBuffer.html
  13. 11 11
      docs/api/core/InterleavedBufferAttribute.html
  14. 1 1
      docs/api/core/Object3D.html
  15. 2 2
      docs/api/core/Raycaster.html
  16. 1 1
      docs/api/helpers/Box3Helper.html
  17. 1 1
      docs/api/helpers/PlaneHelper.html
  18. 16 14
      docs/api/lights/DirectionalLight.html
  19. 1 1
      docs/api/loaders/FontLoader.html
  20. 0 13
      docs/api/loaders/TextureLoader.html
  21. 0 4
      docs/api/loaders/managers/DefaultLoadingManager.html
  22. 2 2
      docs/api/materials/LineBasicMaterial.html
  23. 2 2
      docs/api/materials/LineDashedMaterial.html
  24. 1 2
      docs/api/materials/Material.html
  25. 3 2
      docs/api/materials/MeshBasicMaterial.html
  26. 3 2
      docs/api/materials/MeshDepthMaterial.html
  27. 4 3
      docs/api/materials/MeshLambertMaterial.html
  28. 3 2
      docs/api/materials/MeshNormalMaterial.html
  29. 3 2
      docs/api/materials/MeshPhongMaterial.html
  30. 3 2
      docs/api/materials/MeshStandardMaterial.html
  31. 1 1
      docs/api/materials/PointsMaterial.html
  32. 6 4
      docs/api/materials/ShaderMaterial.html
  33. 1 1
      docs/api/materials/SpriteMaterial.html
  34. 2 2
      docs/api/math/Box2.html
  35. 1 1
      docs/api/math/Cylindrical.html
  36. 7 2
      docs/api/math/Euler.html
  37. 1 6
      docs/api/math/Math.html
  38. 2 11
      docs/api/math/Matrix3.html
  39. 1 5
      docs/api/math/Matrix4.html
  40. 15 12
      docs/api/math/Quaternion.html
  41. 6 0
      docs/api/math/Vector2.html
  42. 4 0
      docs/api/objects/LOD.html
  43. 1 1
      docs/api/objects/Mesh.html
  44. 2 2
      docs/api/renderers/WebGLRenderer.html
  45. 0 39
      docs/api/renderers/webgl/plugins/SpritePlugin.html
  46. 3 3
      docs/api/textures/Texture.html
  47. 3 3
      docs/examples/controls/OrbitControls.html
  48. 81 0
      docs/examples/exporters/ColladaExporter.html
  49. 5 3
      docs/examples/exporters/PLYExporter.html
  50. 6 6
      docs/examples/loaders/GLTFLoader.html
  51. 28 3
      docs/examples/loaders/SVGLoader.html
  52. 1 1
      docs/examples/renderers/SVGRenderer.html
  53. 2 0
      docs/index.html
  54. 6 10
      docs/list.js
  55. 6 6
      editor/css/dark.css
  56. 6 6
      editor/css/light.css
  57. 134 63
      editor/examples/arkanoid.app.json
  58. 106 43
      editor/examples/camera.app.json
  59. 53 17
      editor/examples/particles.app.json
  60. 74 39
      editor/examples/pong.app.json
  61. 8 7
      editor/examples/shaders.app.json
  62. 6 4
      editor/index.html
  63. 19 1
      editor/js/Menubar.File.js
  64. 9 3
      editor/js/Toolbar.js
  65. 0 2
      editor/js/Viewport.js
  66. 1 1
      examples/canvas_ascii_effect.html
  67. 1 1
      examples/canvas_camera_orthographic.html
  68. 1 1
      examples/canvas_geometry_earth.html
  69. 1 1
      examples/canvas_geometry_hierarchy.html
  70. 1 1
      examples/canvas_geometry_panorama.html
  71. 7 5
      examples/canvas_geometry_panorama_fisheye.html
  72. 1 1
      examples/canvas_geometry_shapes.html
  73. 11 10
      examples/canvas_geometry_terrain.html
  74. 1 1
      examples/canvas_geometry_text.html
  75. 1 1
      examples/canvas_interactive_cubes.html
  76. 1 1
      examples/canvas_interactive_cubes_tween.html
  77. 1 1
      examples/canvas_interactive_voxelpainter.html
  78. 42 21
      examples/canvas_lines_colors.html
  79. 25 58
      examples/canvas_lines_dashed.html
  80. 23 20
      examples/canvas_lines_sphere.html
  81. 1 1
      examples/canvas_materials_video.html
  82. 1 1
      examples/canvas_performance.html
  83. 1 1
      examples/canvas_sandbox.html
  84. 7 1
      examples/files.js
  85. 2 4
      examples/index.html
  86. 13 0
      examples/js/Detector.js
  87. 769 752
      examples/js/MarchingCubes.js
  88. 1 1
      examples/js/controls/DragControls.js
  89. 10 8
      examples/js/controls/EditorControls.js
  90. 23 12
      examples/js/controls/MapControls.js
  91. 20 8
      examples/js/controls/OrbitControls.js
  92. 1019 710
      examples/js/controls/TransformControls.js
  93. 596 0
      examples/js/exporters/ColladaExporter.js
  94. 10 0
      examples/js/exporters/GLTFExporter.js
  95. 17 5
      examples/js/exporters/PLYExporter.js
  96. 79 16
      examples/js/loaders/ColladaLoader.js
  97. 1653 1565
      examples/js/loaders/FBXLoader.js
  98. 69 104
      examples/js/loaders/GLTFLoader.js
  99. 1 1
      examples/js/loaders/HDRCubeTextureLoader.js
  100. 74 405
      examples/js/loaders/NodeMaterialLoader.js

File diff suppressed because it is too large
+ 93 - 56
build/three.js


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


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


+ 8 - 3
docs/api/animation/AnimationClip.html

@@ -66,23 +66,28 @@
 		<h2>Methods</h2>
 
 
-		<h3>[method:AnimationClip optimize]()</h3>
+		<h3>[method:this optimize]()</h3>
 		<p>
 			Optimizes each track by removing equivalent sequential keys (which are common in morph target
 			sequences).
 		</p>
 
-		<h3>[method:null resetDuration]()</h3>
+		<h3>[method:this resetDuration]()</h3>
 		<p>
 			Sets the [page:.duration duration] of the clip to the duration of its longest
 			[page:KeyframeTrack].
 		</p>
 
-		<h3>[method:AnimationClip trim]()</h3>
+		<h3>[method:this trim]()</h3>
 		<p>
 			Trims all tracks to the clip's duration.
 		</p>
 
+		<h3>[method:Boolean validate]()</h3>
+		<p>
+			Performs minimal validation on each track in the clip. Returns true if all tracks are valid.
+		</p>
+
 
 		<h2>Static Methods</h2>
 

+ 3 - 4
docs/api/animation/AnimationMixer.html

@@ -55,10 +55,9 @@
 			from the mixer's default root. The first parameter can be either an [page:AnimationClip] object
 			or the name of an AnimationClip.<br /><br />
 
-			If an action fitting these parameters doesn't yet exist, it will be created by this method.<br /><br />
-
-			Note: Calling this method several times with the same parameters returns always the same clip
-			instance.
+			If an action fitting the clip and root parameters doesn't yet exist, it will be created by
+			this method. Calling this method several times with the same clip and root parameters always
+			returns the same clip instance.
 		</p>
 
 		<h3>[method:AnimationAction existingAction]([param:AnimationClip clip], [param:Object3D optionalRoot])</h3>

+ 11 - 9
docs/api/animation/KeyframeTrack.html

@@ -202,13 +202,12 @@
 			created automatically.
 		</p>
 
-		<h3>[method:null optimize]()</h3>
+		<h3>[method:this optimize]()</h3>
 		<p>
-			Removes equivalent sequential keys, which are common in morph target sequences. Called
-			automatically by the constructor.
+			Removes equivalent sequential keys, which are common in morph target sequences.
 		</p>
 
-		<h3>[method:null scale]()</h3>
+		<h3>[method:this scale]()</h3>
 		<p>
 			Scales all keyframe times by a factor.<br /><br />
 
@@ -217,26 +216,29 @@
 			[page:AnimationClip.CreateFromMorphTargetSequence animationClip.CreateFromMorphTargetSequence]).
 		</p>
 
-		<h3>[method:null setInterpolation]( [param:Constant interpolationType] )</h3>
+		<h3>[method:this setInterpolation]( [param:Constant interpolationType] )</h3>
 		<p>
 			Sets the interpolation type. See [page:Animation Animation Constants] for choices.
 		</p>
 
-		<h3>[method:null shift]( [param:Number timeOffsetInSeconds] )</h3>
+		<h3>[method:this shift]( [param:Number timeOffsetInSeconds] )</h3>
 		<p>
 			Moves all keyframes either forward or backward in time.
 		</p>
 
 
-		<h3>[method:null trim]( [param:Number startTimeInSeconds], [param:Number endTimeInSeconds] )</h3>
+		<h3>[method:this trim]( [param:Number startTimeInSeconds], [param:Number endTimeInSeconds] )</h3>
 		<p>
 			Removes keyframes before *startTime* and after *endTime*,
 			without changing any values within the range [*startTime*, *endTime*].
 		</p>
 
-		<h3>[method:null validate]()</h3>
+		<h3>[method:Boolean validate]()</h3>
+		<p>
+			Performs minimal validation on the tracks. Returns true if valid.
+		</p>
+
 		<p>
-			Performs minimal validation on the tracks. Called automatically by the constructor.<br /><br />
 			This method logs errors to the console, if a track is empty, if the [page:.valueSize value size] is not valid, if an item
 			in the [page:.times times] or [page:.values values] array is not a valid number or if the items in the *times* array are out of order.
 		</p>

+ 5 - 5
docs/api/animation/PropertyMixer.html

@@ -18,7 +18,7 @@
 		<h2>Constructor</h2>
 
 
-		<h3>[name]( binding, typeName, valueSize )</h3>
+		<h3>[name]( [param:PropertyBinding binding], [param:String typeName], [param:Number valueSize] )</h3>
 		<p>
 			-- binding <br />
 			-- typeName <br />
@@ -29,12 +29,12 @@
 		<h2>Properties</h2>
 
 
-		<h3>[property:Number binding]</h3>
+		<h3>[property:PropertyBinding binding]</h3>
 		<p>
 
 		</p>
 
-		<h3>[property:Number buffer]</h3>
+		<h3>[property:TypedArray buffer]</h3>
 		<p>
 			Buffer with size [page:PropertyMixer valueSize] * 4. <br /><br />
 			This has the layout: [ incoming | accu0 | accu1 | orig ]<br /><br />
@@ -67,14 +67,14 @@
 		<h2>Methods</h2>
 
 
-		<h3>[method:null accumulate]( accuIndex, weight )</h3>
+		<h3>[method:null accumulate]( [param:Number accuIndex], [param:Number weight] )</h3>
 		<p>
 			Accumulate data in [page:PropertyMixer.buffer buffer][accuIndex] 'incoming' region into 'accu[i]'.<br />
 
 			If weight is *0* this does nothing.
 		</p>
 
-		<h3>[method:null apply]( accuIndex )</h3>
+		<h3>[method:null apply]( [param:Number accuIndex] )</h3>
 		<p>
 			Apply the state of [page:PropertyMixer.buffer buffer] 'accu[i]' to the binding when accus differ.
 		</p>

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

@@ -64,7 +64,7 @@
 			Return a new camera with the same properties as this one.
 		</p>
 
-		<h3>[method:Camera copy]( [param:Camera source] )</h3>
+		<h3>[method:Camera copy]( [param:Camera source], [param:Boolean recursive] )</h3>
 		<p>
 		Copy the properties from the source camera into this one.
 		</p>

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

@@ -55,7 +55,7 @@
 
 		<h2>Methods</h2>
 
-		<h3>[method:null update]( camera )</h3>
+		<h3>[method:null update]( [param:PerspectiveCamera camera] )</h3>
 		<p>
 		Update the stereo cameras based on the camera passed in.
 		</p>

+ 15 - 15
docs/api/core/BufferAttribute.html

@@ -131,28 +131,28 @@
 		<h3>[method:null copyAt] ( [param:Integer index1], [param:BufferAttribute bufferAttribute], [param:Integer index2] ) </h3>
 		<p>Copy a vector from bufferAttribute[index2] to [page:BufferAttribute.array array][index1].</p>
 
-		<h3>[method:BufferAttribute copyColorsArray]( colors ) </h3>
+		<h3>[method:BufferAttribute copyColorsArray]( [param:Array colors] ) </h3>
 		<p>Copy an array representing RGB color values into [page:BufferAttribute.array array].</p>
 
-		<h3>[method:BufferAttribute copyVector2sArray]( vectors ) </h3>
+		<h3>[method:BufferAttribute copyVector2sArray]( [param:Array vectors] ) </h3>
 		<p>Copy an array representing [page:Vector2]s into [page:BufferAttribute.array array].</p>
 
-		<h3>[method:BufferAttribute copyVector3sArray]( vectors ) </h3>
+		<h3>[method:BufferAttribute copyVector3sArray]( [param:Array vectors] ) </h3>
 		<p>Copy an array representing [page:Vector3]s into [page:BufferAttribute.array array].</p>
 
-		<h3>[method:BufferAttribute copyVector4sArray]( vectors ) </h3>
+		<h3>[method:BufferAttribute copyVector4sArray]( [param:Array vectors] ) </h3>
 		<p>Copy an array representing [page:Vector4]s into [page:BufferAttribute.array array].</p>
 
-		<h3>[method:Number getX]( index ) </h3>
+		<h3>[method:Number getX]( [param:Integer index] ) </h3>
 		<p>Returns the x component of the vector at the given index.</p>
 
-		<h3>[method:Number getY]( index ) </h3>
+		<h3>[method:Number getY]( [param:Integer index] ) </h3>
 		<p>Returns the y component of the vector at the given index.</p>
 
-		<h3>[method:Number getZ]( index ) </h3>
+		<h3>[method:Number getZ]( [param:Integer index] ) </h3>
 		<p>Returns the z component of the vector at the given index.</p>
 
-		<h3>[method:Number getW]( index ) </h3>
+		<h3>[method:Number getW]( [param:Integer index] ) </h3>
 		<p>Returns the w component of the vector at the given index.</p>
 
 		<h3>[method:null onUpload]( [param:Function callback] ) </h3>
@@ -185,25 +185,25 @@
 		<h3>[method:BufferAttribute setDynamic] ( [param:Boolean value] ) </h3>
 		<p>Set [page:BufferAttribute.dynamic dynamic] to value.</p>
 
-		<h3>[method:BufferAttribute setX]( index, x ) </h3>
+		<h3>[method:BufferAttribute setX]( [param:Integer index], [param:Float x] ) </h3>
 		<p>Sets the x component of the vector at the given index.</p>
 
-		<h3>[method:BufferAttribute setY]( index, y ) </h3>
+		<h3>[method:BufferAttribute setY]( [param:Integer index], [param:Float y] ) </h3>
 		<p>Sets the y component of the vector at the given index.</p>
 
-		<h3>[method:BufferAttribute setZ]( index, z ) </h3>
+		<h3>[method:BufferAttribute setZ]( [param:Integer index], [param:Float z] ) </h3>
 		<p>Sets the z component of the vector at the given index.</p>
 
-		<h3>[method:BufferAttribute setW]( index, w ) </h3>
+		<h3>[method:BufferAttribute setW]( [param:Integer index], [param:Float w] ) </h3>
 		<p>Sets the w component of the vector at the given index.</p>
 
-		<h3>[method:BufferAttribute setXY]( index, x, y ) </h3>
+		<h3>[method:BufferAttribute setXY]( [param:Integer index], [param:Float x], [param:Float y] ) </h3>
 		<p>Sets the x and y components of the vector at the given index.</p>
 
-		<h3>[method:BufferAttribute setXYZ]( index, x, y, z ) </h3>
+		<h3>[method:BufferAttribute setXYZ]( [param:Integer index], [param:Float x], [param:Float y], [param:Float z] ) </h3>
 		<p>Sets the x, y and z components of the vector at the given index.</p>
 
-		<h3>[method:BufferAttribute setXYZW]( index, x, y, z, w ) </h3>
+		<h3>[method:BufferAttribute setXYZW]( [param:Integer index], [param:Float x], [param:Float y], [param:Float z], [param:Float w] ) </h3>
 		<p>Sets the x, y, z and w components of the vector at the given index.</p>
 
 

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

@@ -91,13 +91,13 @@
 
 		<h2>Methods</h2>
 
-		<h3>[property:null computeGroups]( [page:Geometry geometry] )</h3>
+		<h3>[property:null computeGroups]( [param:Geometry geometry] )</h3>
 		<p>
 			Compute the parts of the geometry that have different materialIndex.
 			See [page:BufferGeometry.groups].
 		</p>
 
-		<h3>[property:null fromGeometry]( [page:Geometry geometry] )</h3>
+		<h3>[property:null fromGeometry]( [param:Geometry geometry] )</h3>
 		<p>Pass in a [page:Geometry] instance for conversion.</p>
 
 

+ 3 - 3
docs/api/core/InterleavedBuffer.html

@@ -91,15 +91,15 @@
 			Set [page:InterleavedBuffer.dynamic dynamic] to value.
 		</p>
 
-		<h3>[method:InterleavedBuffer copy]( source ) </h3>
+		<h3>[method:InterleavedBuffer copy]( [param:InterleavedBuffer source] ) </h3>
 		<p>
 		 Copies another [name] to this [name].
 		</p>
 
-		<h3>[method:InterleavedBuffer copyAt]( index1, attribute, index2 ) </h3>
+		<h3>[method:InterleavedBuffer copyAt]( [param:Integer index1], [param:InterleavedBuffer attribute], [param:Integer index2] ) </h3>
 		<p>Copies data from attribute[index2] to [page:InterleavedBuffer.array array][index1].</p>
 
-		<h3>[method:InterleavedBuffer set]( value, offset ) </h3>
+		<h3>[method:InterleavedBuffer set]( [param:TypedArray value], [param:Integer offset] ) </h3>
 		<p>
 			value - The source (typed) array.<br/>
 			offset - The offset into the target array at which to begin writing values from the source array. Default is *0*.<br/><br />

+ 11 - 11
docs/api/core/InterleavedBufferAttribute.html

@@ -62,37 +62,37 @@
 
 		<h2>Methods</h2>
 
-		<h3>[method:Number getX]( index ) </h3>
+		<h3>[method:Number getX]( [param:Integer index] ) </h3>
 		<p>Returns the x component of the item at the given index.</p>
 
-		<h3>[method:Number getY]( index ) </h3>
+		<h3>[method:Number getY]( [param:Integer index] ) </h3>
 		<p>Returns the y component of the item at the given index.</p>
 
-		<h3>[method:Number getZ]( index ) </h3>
+		<h3>[method:Number getZ]( [param:Integer index] ) </h3>
 		<p>Returns the z component of the item at the given index.</p>
 
-		<h3>[method:Number getW]( index ) </h3>
+		<h3>[method:Number getW]( [param:Integer index] ) </h3>
 		<p>Returns the w component of the item at the given index.</p>
 
-		<h3>[method:null setX]( index, x ) </h3>
+		<h3>[method:null setX]( [param:Integer index], [param:Float x] ) </h3>
 		<p>Sets the x component of the item at the given index.</p>
 
-		<h3>[method:null setY]( index, y ) </h3>
+		<h3>[method:null setY]( [param:Integer index], [param:Float y] ) </h3>
 		<p>Sets the y component of the item at the given index.</p>
 
-		<h3>[method:null setZ]( index, z ) </h3>
+		<h3>[method:null setZ]( [param:Integer index], [param:Float z] ) </h3>
 		<p>Sets the z component of the item at the given index.</p>
 
-		<h3>[method:null setW]( index, w ) </h3>
+		<h3>[method:null setW]( [param:Integer index], [param:Float w] ) </h3>
 		<p>Sets the w component of the item at the given index.</p>
 
-		<h3>[method:null setXY]( index, x, y ) </h3>
+		<h3>[method:null setXY]( [param:Integer index], [param:Float x], [param:Float y] ) </h3>
 		<p>Sets the x and y components of the item at the given index.</p>
 
-		<h3>[method:null setXYZ]( index, x, y, z ) </h3>
+		<h3>[method:null setXYZ]( [param:Integer index], [param:Float x], [param:Float y], [param:Float z] ) </h3>
 		<p>Sets the x, y and z components of the item at the given index.</p>
 
-		<h3>[method:null setXYZW]( index, x, y, z, w ) </h3>
+		<h3>[method:null setXYZW]( [param:Integer index], [param:Float x], [param:Float y], [param:Float z], [param:Float w] ) </h3>
 		<p>Sets the x, y, z and w components of the item at the given index.</p>
 
 

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

@@ -39,7 +39,7 @@
 		<h3>[property:Boolean frustumCulled]</h3>
 		<p>
 		When this is set, it checks every frame if the object is in the frustum of the camera before rendering the object.
-		Otherwise the object gets renderered every frame even if it isn't visible. Default is *true*.
+		Otherwise the object gets rendered every frame even if it isn't visible. Default is *true*.
 		</p>
 
 		<h3>[property:Integer id]</h3>

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

@@ -78,7 +78,7 @@
 		[page:Vector3 origin] — The origin vector where the ray casts from.<br />
 		[page:Vector3 direction] — The direction vector that gives direction to the ray. Should be normalized.<br />
 		[page:Float near] — All results returned are further away than near. Near can't be negative. Default value is 0.<br />
-		[page:Float far] — All results returned are closer then far. Far can't be lower then near . Default value is Infinity.
+		[page:Float far] — All results returned are closer than far. Far can't be lower than near. Default value is Infinity.
 		</p>
 		<p>
 		This creates a new raycaster object.<br />
@@ -144,7 +144,7 @@
 		Updates the ray with a new origin and direction.
 		</p>
 
-		<h3>[method:Array intersectObject]( [page:Object3D object], [param:Boolean recursive], [param:Array optionalTarget] )</h3>
+		<h3>[method:Array intersectObject]( [param:Object3D object], [param:Boolean recursive], [param:Array optionalTarget] )</h3>
 		<p>
 		[page:Object3D object] — The object to check for intersection with the ray.<br />
 		[page:Boolean recursive] — If true, it also checks all descendants. Otherwise it only checks intersecton with the object. Default is false.<br />

+ 1 - 1
docs/api/helpers/Box3Helper.html

@@ -50,7 +50,7 @@
 		<p>See the base [page:LineSegments] class for common methods.</p>
 
 
-		<h3>[method:void updateMatrixWorld]( force )</h3>
+		<h3>[method:void updateMatrixWorld]( [param:Boolean force] )</h3>
 		<p>
 			This overrides the method in the base [page:Object3D] class so that it
 			also updates the wireframe box to the extent of the [page:Box3Helper.box .box]

+ 1 - 1
docs/api/helpers/PlaneHelper.html

@@ -51,7 +51,7 @@
 		<h2>Methods</h2>
 		<p>See the base [page:LineSegments] class for common methods.</p>
 
-		<h3>[method:void updateMatrixWorld]( force )</h3>
+		<h3>[method:void updateMatrixWorld]( [param:Boolean force] )</h3>
 		<p>
 			This overrides the method in the base [page:Object3D] class so that it also
 			updates the helper object according to the [page:PlaneHelper.plane .plane] and

+ 16 - 14
docs/api/lights/DirectionalLight.html

@@ -52,10 +52,10 @@
 		</p>
 
 		<code>
-// White directional light at half intensity shining from the top.
-var directionalLight = new THREE.DirectionalLight( 0xffffff, 0.5 );
-scene.add( directionalLight );
-	</code>
+		// White directional light at half intensity shining from the top.
+		var directionalLight = new THREE.DirectionalLight( 0xffffff, 0.5 );
+		scene.add( directionalLight );
+		</code>
 
 
 		<h2>Constructor</h2>
@@ -104,26 +104,28 @@ scene.add( directionalLight );
 
 			*Note*: For the target's position to be changed to anything other than the default,
 			it must be added to the [page:Scene scene] using
-			<code>
-				scene.add( light.target );
-			</code>
-
+		</p>
+		<code>
+		scene.add( light.target );
+		</code>
+		<p>
 			This is so that the target's [page:Object3D.matrixWorld matrixWorld] gets automatically
 			updated each frame.<br /><br />
 
 			It is also possible to set the target to be another object in the scene (anything with a
 			[page:Object3D.position position] property), like so:
-			<code>
-	var targetObject = new THREE.Object3D();
-	scene.add(targetObject);
+		</p>
+		<code>
+		var targetObject = new THREE.Object3D();
+		scene.add(targetObject);
 
-	light.target = targetObject;
-			</code>
+		light.target = targetObject;
+		</code>
+		<p>
 			The directionalLight will now track the target object.
 		</p>
 
 
-
 		<h2>Methods</h2>
 
 		See the base [page:Light Light] class for common methods.

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

@@ -29,7 +29,7 @@
 		var loader = new THREE.FontLoader();
 		var font = loader.load(
 			// resource URL
-			'fonts/helvetiker_bold.typeface.json'
+			'fonts/helvetiker_bold.typeface.json',
 
 			// onLoad callback
 			function ( font ) {

+ 0 - 13
docs/api/loaders/TextureLoader.html

@@ -85,12 +85,6 @@
 		<p>The base path from which files will be loaded. See [page:.setPath]. Default is *undefined*.</p>
 
 
-		<h3>[property:String withCredentials]</h3>
-		<p>
-			Whether the XMLHttpRequest uses credentials - see [page:.setWithCredentials].
-			Default is *undefined*.
-		</p>
-
 		<h2>Methods</h2>
 
 		<h3>[method:Texture load]( [param:String url], [param:Function onLoad], [param:Function onProgress], [param:Function onError] )</h3>
@@ -114,13 +108,6 @@
 			you are loading many models from the same directory.
 		</p>
 
-		<h3>[method:FileLoader setWithCredentials]( [param:Boolean value] )</h3>
-		<p>
-		Whether the XMLHttpRequest uses credentials such as cookies, authorization headers or
-		TLS client certificates. See
-		[link:https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/withCredentials XMLHttpRequest.withCredentials].<br />
-		Note that this has no effect if you are loading files locally or from the same domain.
-		</p>
 
 		<h2>Source</h2>
 

+ 0 - 4
docs/api/loaders/managers/DefaultLoadingManager.html

@@ -19,10 +19,6 @@
 
 		<h2>Example</h2>
 
-		<p>
-			[example:webgl_loader_scene WebGL / loader / scene]<br />
-		</p>
-
 		<p>
 			You can optionally set the [page:LoadingManager.onStart onStart], [page:LoadingManager.onLoad onLoad],
 			[page:LoadingManager.onProgress onProgress], [page:LoadingManager.onStart onError] functions for the manager.

+ 2 - 2
docs/api/materials/LineBasicMaterial.html

@@ -75,8 +75,8 @@ var material = new THREE.LineBasicMaterial( {
 		<p>
 			Controls line thickness. Default is *1*.<br /><br />
 
-			Due to limitations in the [link:https://code.google.com/p/angleproject ANGLE layer],
-			with the [page:WebGLRenderer WebGL] renderer on Windows platforms linewidth will
+			Due to limitations of the [link:https://www.khronos.org/registry/OpenGL/specs/gl/glspec46.core.pdf OpenGL Core Profile]
+			with the [page:WebGLRenderer WebGL] renderer on most platforms linewidth will
 			always be 1 regardless of the set value.
 		</p>
 

+ 2 - 2
docs/api/materials/LineDashedMaterial.html

@@ -70,8 +70,8 @@ var material = new THREE.LineDashedMaterial( {
 		<p>
 			Controls line thickness. Default is *1*.<br /><br />
 
-			Due to limitations in the [link:https://code.google.com/p/angleproject ANGLE layer],
-			with the [page:WebGLRenderer WebGL] renderer on Windows platforms linewidth will
+			Due to limitations of the [link:https://www.khronos.org/registry/OpenGL/specs/gl/glspec46.core.pdf OpenGL Core Profile]
+			with the [page:WebGLRenderer WebGL] renderer on most platforms linewidth will
 			always be 1 regardless of the set value.
 		</p>
 

+ 1 - 2
docs/api/materials/Material.html

@@ -162,8 +162,7 @@
 
 		<h3>[property:Boolean needsUpdate]</h3>
 		<p>
-		Specifies that the material needs to be updated at the WebGL level.
-		Set it to true if you made changes that need to be reflected in WebGL.<br />
+		Specifies that the material needs to be recompiled.<br />
 		This property is automatically set to *true* when instancing a new material.
 		</p>
 

+ 3 - 2
docs/api/materials/MeshBasicMaterial.html

@@ -151,8 +151,9 @@
 		<h3>[property:Float wireframeLinewidth]</h3>
 		<p>Controls wireframe thickness. Default is 1.<br /><br />
 
-		Due to limitations in the [link:https://code.google.com/p/angleproject ANGLE layer],
-		on Windows platforms linewidth will always be 1 regardless of the set value.
+		Due to limitations of the [link:https://www.khronos.org/registry/OpenGL/specs/gl/glspec46.core.pdf OpenGL Core Profile]
+		with the [page:WebGLRenderer WebGL] renderer on most platforms linewidth will
+		always be 1 regardless of the set value.
 		</p>
 
 		<h2>Methods</h2>

+ 3 - 2
docs/api/materials/MeshDepthMaterial.html

@@ -107,8 +107,9 @@
 		<h3>[property:Float wireframeLinewidth]</h3>
 		<p>Controls wireframe thickness. Default is 1.<br /><br />
 
-		Due to limitations in the [link:https://code.google.com/p/angleproject ANGLE layer],
-		on Windows platforms linewidth will always be 1 regardless of the set value.
+		Due to limitations of the [link:https://www.khronos.org/registry/OpenGL/specs/gl/glspec46.core.pdf OpenGL Core Profile]
+		with the [page:WebGLRenderer WebGL] renderer on most platforms linewidth will
+		always be 1 regardless of the set value.
 		</p>
 
 		<h2>Methods</h2>

+ 4 - 3
docs/api/materials/MeshLambertMaterial.html

@@ -101,7 +101,7 @@
 
 		<h3>[property:Texture emissiveMap]</h3>
 		<p>
-		Set emisssive (glow) map. Default is null. The emissive map color is modulated by
+		Set emissive (glow) map. Default is null. The emissive map color is modulated by
 		the emissive color and the emissive intensity. If you have an emissive map, be sure to
 		set the emissive color to something other than black.
 		</p>
@@ -177,8 +177,9 @@
 		<h3>[property:Float wireframeLinewidth]</h3>
 		<p>Controls wireframe thickness. Default is 1.<br /><br />
 
-		Due to limitations in the [link:https://code.google.com/p/angleproject ANGLE layer],
-		on Windows platforms linewidth will always be 1 regardless of the set value.
+		Due to limitations of the [link:https://www.khronos.org/registry/OpenGL/specs/gl/glspec46.core.pdf OpenGL Core Profile]
+		with the [page:WebGLRenderer WebGL] renderer on most platforms linewidth will
+		always be 1 regardless of the set value.
 		</p>
 
 		<h2>Methods</h2>

+ 3 - 2
docs/api/materials/MeshNormalMaterial.html

@@ -68,8 +68,9 @@
 		<h3>[property:Float wireframeLinewidth]</h3>
 		<p>Controls wireframe thickness. Default is 1.<br /><br />
 
-		Due to limitations in the [link:https://code.google.com/p/angleproject ANGLE layer],
-		on Windows platforms linewidth will always be 1 regardless of the set value.
+		Due to limitations of the [link:https://www.khronos.org/registry/OpenGL/specs/gl/glspec46.core.pdf OpenGL Core Profile]
+		with the [page:WebGLRenderer WebGL] renderer on most platforms linewidth will
+		always be 1 regardless of the set value.
 		</p>
 
 		<h2>Methods</h2>

+ 3 - 2
docs/api/materials/MeshPhongMaterial.html

@@ -247,8 +247,9 @@
 		<h3>[property:Float wireframeLinewidth]</h3>
 		<p>Controls wireframe thickness. Default is 1.<br /><br />
 
-		Due to limitations in the [link:https://code.google.com/p/angleproject ANGLE layer],
-		on Windows platforms linewidth will always be 1 regardless of the set value.
+		Due to limitations of the [link:https://www.khronos.org/registry/OpenGL/specs/gl/glspec46.core.pdf OpenGL Core Profile]
+		with the [page:WebGLRenderer WebGL] renderer on most platforms linewidth will
+		always be 1 regardless of the set value.
 		</p>
 
 		<h2>Methods</h2>

+ 3 - 2
docs/api/materials/MeshStandardMaterial.html

@@ -274,8 +274,9 @@
 		<h3>[property:Float wireframeLinewidth]</h3>
 		<p>Controls wireframe thickness. Default is 1.<br /><br />
 
-		Due to limitations in the [link:https://code.google.com/p/angleproject ANGLE layer],
-		on Windows platforms linewidth will always be 1 regardless of the set value.
+		Due to limitations of the [link:https://www.khronos.org/registry/OpenGL/specs/gl/glspec46.core.pdf OpenGL Core Profile]
+		with the [page:WebGLRenderer WebGL] renderer on most platforms linewidth will
+		always be 1 regardless of the set value.
 		</p>
 
 		<h2>Methods</h2>

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

@@ -55,7 +55,7 @@ var starField = new THREE.Points( starsGeometry, starsMaterial );
 scene.add( starField );
 		</code>
 
-		<h3>[name]( [page:Object parameters] )</h3>
+		<h3>[name]( [param:Object parameters] )</h3>
 		<p>
 			[page:Object parameters] - (optional) an object with one or more properties defining the material's appearance.
 			Any property of the material (including any property inherited from [page:Material]) can be passed in here.<br /><br />

+ 6 - 4
docs/api/materials/ShaderMaterial.html

@@ -371,8 +371,9 @@ this.extensions = {
 		<h3>[property:Float linewidth]</h3>
 		<p>Controls wireframe thickness. Default is 1.<br /><br />
 
-		Due to limitations in the [link:https://code.google.com/p/angleproject ANGLE layer],
-		on Windows platforms linewidth will always be 1 regardless of the set value.
+		Due to limitations of the [link:https://www.khronos.org/registry/OpenGL/specs/gl/glspec46.core.pdf OpenGL Core Profile]
+		with the [page:WebGLRenderer WebGL] renderer on most platforms linewidth will
+		always be 1 regardless of the set value.
 		</p>
 
 
@@ -443,8 +444,9 @@ this.extensions = {
 		<h3>[property:Float wireframeLinewidth]</h3>
 		<p>Controls wireframe thickness. Default is 1.<br /><br />
 
-		Due to limitations in the [link:https://code.google.com/p/angleproject ANGLE layer],
-		on Windows platforms linewidth will always be 1 regardless of the set value.
+		Due to limitations of the [link:https://www.khronos.org/registry/OpenGL/specs/gl/glspec46.core.pdf OpenGL Core Profile)
+		with the [page:WebGLRenderer WebGL] renderer on most platforms linewidth will
+		always be 1 regardless of the set value.
 		</p>
 
 

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

@@ -37,7 +37,7 @@ scene.add( sprite );
 		</code>
 
 
-		<h3>[name]( [page:Object parameters] )</h3>
+		<h3>[name]( [param:Object parameters] )</h3>
 		<p>
 			[page:Object parameters] - (optional) an object with one or more properties defining the material's appearance.
 			Any property of the material (including any property inherited from [page:Material]) can be passed in here.<br /><br />

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

@@ -23,7 +23,7 @@
 		[page:Vector2 min] - (optional) [page:Vector2] representing the lower (x, y) boundary of the box.
 		Default is ( + Infinity, + Infinity ).<br>
 
-		[page:Vector2 max] - (optional) [page:Vector2] representing the lower upper (x, y) boundary of the box.
+		[page:Vector2 max] - (optional) [page:Vector2] representing the upper (x, y) boundary of the box.
 		Default is ( - Infinity, - Infinity ).<br /><br />
 
 		Creates a [name] bounded by min and max.
@@ -172,7 +172,7 @@
 		<h3>[method:Box2 set]( [param:Vector2 min], [param:Vector2 max] )</h3>
 		<p>
 			[page:Vector2 min] - (required ) [page:Vector2] representing the lower (x, y) boundary of the box. <br>
-			[page:Vector2 max]  - (required) [page:Vector2] representing the lower upper (x, y) boundary of the box. <br /><br />
+			[page:Vector2 max]  - (required) [page:Vector2] representing the upper (x, y) boundary of the box. <br /><br />
 
 			Sets the lower and upper (x, y) boundaries of this box.
 		</p>

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

@@ -50,7 +50,7 @@
 			and [page:.y y] properties to this cylindrical.
 		</p>
 
-		<h3>[method:Cylindrical set]( [param:Float radius], [param:Float phi], [param:Float theta] )</h3>
+		<h3>[method:Cylindrical set]( [param:Float radius], [param:Float theta], [param:Float y] )</h3>
 		<p>Sets values of this cylindrical's [page:.radius radius], [page:.theta theta]
 		and [page:.y y] properties.</p>
 

+ 7 - 2
docs/api/math/Euler.html

@@ -176,8 +176,13 @@
 		Returns an array of the form [[page:.x x], [page:.y y], [page:.z z], [page:.order order ]].
 		</p>
 
-		<h3>[method:Vector3 toVector3]()</h3>
-		<p>Returns the Euler's [page:.x x], [page:.y y] and [page:.z z] properties as a [page:Vector3].</p>
+		<h3>[method:Vector3 toVector3]( [param:Vector3 optionalResult] )</h3>
+		<p>
+			[page:Vector3 optionalResult] — (optional) If specified, the result will be copied into this Vector,
+			otherwise a new one will be created. <br /><br />
+
+			Returns the Euler's [page:.x x], [page:.y y] and [page:.z z] properties as a [page:Vector3].
+		</p>
 
 
 		<h2>Source</h2>

+ 1 - 6
docs/api/math/Math.html

@@ -54,12 +54,7 @@
 		and [page:Float t] = 1 will return [page:Float y].
 		</p>
 
-		<h3>[method:Float mapLinear](
-			[page:Float x],
-			[page:Float a1],
-			[page:Float a2],
-			[page:Float b1],
-			[page:Float b2] )</h3>
+		<h3>[method:Float mapLinear]( [param:Float x], [param:Float a1], [param:Float a2], [param:Float b1], [param:Float b2] )</h3>
 		<p>
 		[page:Float x] — Value to be mapped.<br />
 		[page:Float a1] — Minimum value for range A.<br />

+ 2 - 11
docs/api/math/Matrix3.html

@@ -144,12 +144,7 @@ m.elements = [ 11, 21, 31,
 		<h3>[method:this multiplyScalar]( [param:Float s] )</h3>
 		<p>Multiplies every component of the matrix by the scalar value *s*.</p>
 
-		<h3>
-			[method:this set](
-			[page:Float n11], [page:Float n12], [page:Float n13],
-			[page:Float n21], [page:Float n22], [page:Float n23],
-			[page:Float n31], [page:Float n32], [page:Float n33] )
-		</h3>
+		<h3>[method:this set]( [param:Float n11], [param:Float n12], [param:Float n13], [param:Float n21], [param:Float n22], [param:Float n23], [param:Float n31], [param:Float n32], [param:Float n33] )</h3>
 		<p>
 		[page:Float n11] - value to put in row 1, col 1.<br />
 		[page:Float n12] - value to put in row 1, col 2.<br />
@@ -169,11 +164,7 @@ m.elements = [ 11, 21, 31,
 		<h3>[method:this setFromMatrix4]( [param:Matrix4 m] )</h3>
 		<p>Set this matrx to the upper 3x3 matrix of the Matrix4 [page:Matrix4 m].</p>
 
-		<h3>
-			[method:this setUvTransform](
-			[page:Float tx], [page:Float ty], [page:Float sx], [page:Float sy],
-			[page:Float rotation], [page:Float cx], [page:Float cy] )
-		</h3>
+		<h3>[method:this setUvTransform]( [param:Float tx], [param:Float ty], [param:Float sx], [param:Float sy], [param:Float rotation], [param:Float cx], [param:Float cy] )</h3>
 		<p>
 		[page:Float tx] - offset x<br />
 		[page:Float ty] - offset y<br />

+ 1 - 5
docs/api/math/Matrix4.html

@@ -368,11 +368,7 @@ x, y, 1, 0,
 		<h3>[method:this scale]( [param:Vector3 v] )</h3>
 		<p>Multiplies the columns of this matrix by vector [page:Vector3 v].</p>
 
-		<h3>[method:this set](
-			[page:Float n11], [page:Float n12], [page:Float n13], [page:Float n14],
-			[page:Float n21], [page:Float n22], [page:Float n23], [page:Float n24],
-			[page:Float n31], [page:Float n32], [page:Float n33], [page:Float n34],
-			[page:Float n41], [page:Float n42], [page:Float n43], [page:Float n44] )</h3>
+		<h3>[method:this set]( [param:Float n11], [param:Float n12], [param:Float n13], [param:Float n14], [param:Float n21], [param:Float n22], [param:Float n23], [param:Float n24], [param:Float n31], [param:Float n32], [param:Float n33], [param:Float n34], [param:Float n41], [param:Float n42], [param:Float n43], [param:Float n44] )</h3>
 		<p>
 			Set the [page:.elements elements] of this matrix to the supplied row-major values [page:Float n11],
 			[page:Float n12], ... [page:Float n44].

+ 15 - 12
docs/api/math/Quaternion.html

@@ -59,13 +59,17 @@
 
 		<h2>Methods</h2>
 
+		<h3>[method:Float angleTo]( [param:Quaternion q] )</h3>
+		<p>
+			Returns the angle between this quaternion and quaternion [page:Quaternion q] in radians.
+		</p>
+
 		<h3>[method:Quaternion clone]()</h3>
 		<p>
 			Creates a new Quaternion with identical [page:.x x], [page:.y y],
 			[page:.z z] and [page:.w w] properties to this one.
 		</p>
 
-
 		<h3>[method:Quaternion conjugate]()</h3>
 		<p>
 		Returns the rotational conjugate of this quaternion. The conjugate of a quaternion
@@ -165,6 +169,15 @@
 		<h3>[method:Quaternion premultiply]( [param:Quaternion q] )</h3>
 		<p>Pre-multiplies this quaternion by [page:Quaternion q].</p>
 
+		<h3>[method:Quaternion rotateTowards]( [param:Quaternion q], [param:Float step] )</h3>
+		<p>
+			[page:Quaternion q] - The target quaternion.<br />
+			[page:float step] - The angular step in radians.<br /><br />
+
+			Rotates this quaternion by a given angular step to the defined quaternion *q*.
+			The method ensures that the final quaternion will not overshoot *q*.
+		</p>
+
 		<h3>[method:Quaternion slerp]( [param:Quaternion qb], [param:float t] )</h3>
 		<p>
 			[page:Quaternion qb] - The other quaternion rotation<br />
@@ -257,17 +270,7 @@ q.slerp( qb, t )
 		</p>
 
 
-		<h3>
-		[method:null slerpFlat](
-			[page:Array dst],
-			[page:Integer dstOffset],
-			[page:Array src0],
-			[page:Integer srcOffset0],
-			[page:Array src1],
-			[page:Integer srcOffset1],
-			[page:Float t]
-		)
-		</h3>
+		<h3>[method:null slerpFlat]( [param:Array dst], [param:Integer dstOffset], [param:Array src0], [param:Integer srcOffset0], [param:Array src1], [param:Integer srcOffset1], [param:Float t] )</h3>
 		<p>
 		[page:Array dst] - The output array.<br />
 		[page:Integer dstOffset] - An offset into the output array.<br />

+ 6 - 0
docs/api/math/Vector2.html

@@ -176,6 +176,12 @@
 		Calculates the [link:https://en.wikipedia.org/wiki/Dot_product dot product] of this
 	  vector and [page:Vector2 v].
 		</p>
+        
+		<h3>[method:Float cross]( [param:Vector2 v] )</h3>
+		<p>
+		Calculates the [link:https://en.wikipedia.org/wiki/Cross_product cross product] of this
+	  vector and [page:Vector2 v]. Note that a 'cross-product' in 2D is not well-defined. This function computes a geometric cross-product often used in 2D graphics
+		</p>
 
 		<h3>[method:Boolean equals]( [param:Vector2 v] )</h3>
 		<p>Checks for strict equality of this vector and [page:Vector2 v].</p>

+ 4 - 0
docs/api/objects/LOD.html

@@ -21,6 +21,10 @@
 		</p>
 
 		<h2>Example</h2>
+		
+		<p>
+			[example:webgl_lod webgl / lod ]
+		</p>
 
 		<code>
 var lod = new THREE.LOD();

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

@@ -86,7 +86,7 @@
 		<h2>Methods</h2>
 		<p>See the base [page:Object3D] class for common methods.</p>
 
-		<h3>[method:null setDrawMode]()</h3>
+		<h3>[method:null setDrawMode]( [param:Integer value] )</h3>
 		<p>Set the value of [page:.drawMode drawMode].</p>
 
 		<h3>[method:Mesh clone]()</h3>

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

@@ -211,7 +211,7 @@
 			Keep in mind that the standard materials only allow 4 MorphNormals.
 		</p>
 
-		<h3>[property:Integer physicallyCorrectLights]</h3>
+		<h3>[property:Boolean physicallyCorrectLights]</h3>
 		<p>
 		Whether to use physically correct lighting mode. Default is *false*.
 		See the [example:webgl_lights_physical lights / physical] example.
@@ -392,7 +392,7 @@
 			either the [page:WebGLRenderer.autoClearColor autoClearColor], [page:WebGLRenderer.autoClearStencil autoClearStencil] or [page:WebGLRenderer.autoClearDepth autoClearDepth] properties to false.
 		</p>
 
-		<h3>[method:null renderBufferDirect]( [param:Camera camera], [param:Array lights], [param:Fog fog], [param:Material material], [param:Object geometryGroup], [param:Object3D object] )</h3>
+		<h3>[method:null renderBufferDirect]( [param:Camera camera], [param:Fog fog], [param:Geometry geometry], [param:Material material], [param:Object3D object], [param:Object group] )</h3>
 		<p>Render a buffer geometry group using the camera and with the specified material.</p>
 
 		<h3>[method:null renderBufferImmediate]( [param:Object3D object], [param:shaderprogram program], [param:Material shading] )</h3>

+ 0 - 39
docs/api/renderers/webgl/plugins/SpritePlugin.html

@@ -1,39 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-	<head>
-		<meta charset="utf-8" />
-		<base href="../../../../" />
-		<script src="list.js"></script>
-		<script src="page.js"></script>
-		<link type="text/css" rel="stylesheet" href="page.css" />
-	</head>
-	<body>
-		<h1>[name]</h1>
-
-		<p class="desc">The Webglrenderer plugin class that allows Sprites to be rendered in the WebglRenderer. This plugin is automatically loaded in the Webglrenderer.</p>
-
-
-		<h2>Constructor</h2>
-
-		<h3>[name]()</h3>
-		<p>
-		Creates a new [name].
-		</p>
-
-
-		<h2>Methods</h2>
-
-		<h3>[method:null render]( [param:Scene scene], [param:Camera camera] )</h3>
-		<p>
-		scene -- The scene to render. <br />
-		camera -- The camera to render.
-		</p>
-		<p>
-		Renders the sprites defined in the scene. This gets automatically called as post-render function to draw the lensflares.
-		</p>
-
-		<h2>Source</h2>
-
-		[link:https://github.com/mrdoob/three.js/blob/master/src/[path].js src/[path].js]
-	</body>
-</html>

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

@@ -234,12 +234,12 @@
 		[page:Texture.rotation .rotation], and [page:Texture.center .center].
 		</p>
 
-		<h3>[method:Texture clone]( [param:Texture texture] )</h3>
+		<h3>[method:Texture clone]()</h3>
 		<p>
 		Make copy of the texture. Note this is not a "deep copy", the image is shared.
 		</p>
 
-		<h3>[method:Texture toJSON]( meta )</h3>
+		<h3>[method:Texture toJSON]( [param:Object meta] )</h3>
 		<p>
 		meta -- optional object containing metadata.<br />
 		Convert the material to three.js JSON format.
@@ -250,7 +250,7 @@
 		Call [page:EventDispatcher EventDispatcher].dispatchEvent with a 'dispose' event type.
 		</p>
 
-		<h3>[method:null transformUv]( uv )</h3>
+		<h3>[method:null transformUv]( [param:Vector2 uv] )</h3>
 		<p>
 		Transform the uv based on the value of this texture's [page:Texture.offset .offset], [page:Texture.repeat .repeat],
 		[page:Texture.wrapS .wrapS], [page:Texture.wrapT .wrapT] and [page:Texture.flipY .flipY] properties.

+ 3 - 3
docs/examples/controls/OrbitControls.html

@@ -190,9 +190,9 @@ controls.keys = {
 			This object contains references to the mouse buttons used for the controls.
 			<code>
 controls.mouseButtons = {
-	ORBIT: THREE.MOUSE.LEFT,
-	ZOOM: THREE.MOUSE.MIDDLE,
-	PAN: THREE.MOUSE.RIGHT
+	LEFT: THREE.MOUSE.LEFT,
+	MIDDLE: THREE.MOUSE.MIDDLE,
+	RIGHT: THREE.MOUSE.RIGHT
 }
 			</code>
 		</p>

+ 81 - 0
docs/examples/exporters/ColladaExporter.html

@@ -0,0 +1,81 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<meta charset="utf-8" />
+		<base href="../../" />
+		<script src="list.js"></script>
+		<script src="page.js"></script>
+		<link type="text/css" rel="stylesheet" href="page.css" />
+	</head>
+	<body>
+		<h1>[name]</h1>
+
+		<p class="desc">
+		An exporter for *Collada*.
+		<br /><br />
+		<a href="https://www.khronos.org/collada/">Collada</a> is a
+		file format for robust representation of scenes, materials, animations, and other 3D content in an xml format.
+		This exporter only supports exporting geometry, materials, textures, and scene hierarchy.
+		</p>
+
+		<h2>Example</h2>
+
+		<code>
+		// Instantiate an exporter
+		var exporter = new THREE.ColladaExporter();
+
+		// Parse the input and generate the ply output
+		var data = exporter.parse( scene, null, options );
+		downloadFile(data);
+		</code>
+
+		<h2>Constructor</h2>
+
+		<h3>[name]()</h3>
+		<p>
+		</p>
+		<p>
+		Creates a new [name].
+		</p>
+
+		<h2>Methods</h2>
+
+		<h3>[method:null parse]( [param:Object3D input], [param:Function onCompleted], [param:Object options] )</h3>
+		<p>
+		[page:Object input] — Object3D to be exported<br />
+		[page:Function onCompleted] — Will be called when the export completes. Optional. The same data is immediately returned from the function.<br />
+		[page:Options options] — Export options<br />
+		<ul>
+			<li>version - string. Which version of Collada to export. The options are "1.4.1" or "1.5.0". Defaults to "1.4.1".</li>
+			<li>author - string. The name to include in the author field. Author field is excluded by default.</li>
+			<li>textureDirectory - string. The directory relative to the Collada file to save the textures to.</li>
+		</ul>
+		</p>
+		<p>
+		Generates an object with Collada file and texture data. This object is returned from the function and passed into the "onCompleted" callback.
+		<code>
+		{
+			// Collada file content
+			data: "",
+
+			// List of referenced texures
+			textures: [{
+
+				// File directory, name, and extension of the texture data
+				directory: "",
+				name: "",
+				ext: "",
+
+				// The texture data and original texture object
+				data: [],
+				original: &ltTHREE.Texture&gt
+			}, ...]
+		}
+		</code>
+		</p>
+
+		<h2>Source</h2>
+
+		[link:https://github.com/mrdoob/three.js/blob/master/examples/js/exporters/ColladaExporter.js examples/js/exporters/ColladaExporter.js]
+	</body>
+</html>

+ 5 - 3
docs/examples/exporters/PLYExporter.html

@@ -13,7 +13,7 @@
 		<p class="desc">
 		An exporter for *PLY*.
 		<br /><br />
-		<a href="https://www.khronos.org/gltf">PLY</a> (Polygon or Stanford Triangle Format) is a
+		<a href="https://en.wikipedia.org/wiki/PLY_(file_format)">PLY</a> (Polygon or Stanford Triangle Format) is a
 		file format for efficient delivery and loading of simple, static 3D content in a dense format.
 		Both binary and ascii formats are supported. PLY can store vertex positions, colors, normals and
 		uv coordinates. No textures or texture references are saved.
@@ -41,9 +41,10 @@
 
 		<h2>Methods</h2>
 
-		<h3>[method:null parse]( [param:Object3D input], [param:Object options] )</h3>
+		<h3>[method:null parse]( [param:Object3D input], [param:Function onDone], [param:Object options] )</h3>
 		<p>
 		[page:Object input] — Object3D<br />
+		[page:Function onCompleted] — Will be called when the export completes. The argument will be the generated ply ascii or binary ArrayBuffer.<br />
 		[page:Options options] — Export options<br />
 		<ul>
 			<li>excludeAttributes - array. Which properties to explicitly exclude from the exported PLY file. Valid values are 'color', 'normal', 'uv', and 'index'. If triangle indices are excluded, then a point cloud is exported. Default is an empty array.</li>
@@ -51,7 +52,8 @@
 		</ul>
 		</p>
 		<p>
-		Generates ply file data as string or ArrayBuffer (ascii or binary) output from the input object.
+		Generates ply file data as string or ArrayBuffer (ascii or binary) output from the input object. The data that is returned is the same
+		that is passed into the "onCompleted" function.
 		If the object is composed of multiple children and geometry, they are merged into a single mesh in the file.
 		</p>
 

+ 6 - 6
docs/examples/loaders/GLTFLoader.html

@@ -12,8 +12,8 @@
 		<h1>[name]</h1>
 
 		<p class="desc"> A loader for <em>glTF 2.0</em> resources. <br /><br />
-		<a href="https://www.khronos.org/gltf">glTF</a> (GL Transmission Format) is an
-		<a href="https://github.com/KhronosGroup/glTF/tree/master/specification/2.0">open format specification</a>
+		[link:https://www.khronos.org/gltf glTF] (GL Transmission Format) is an
+		[link:https://github.com/KhronosGroup/glTF/tree/master/specification/2.0 open format specification]
 		for efficient delivery and loading of 3D content. Assets may be provided either in JSON (.gltf)
 		or binary (.glb) format. External files store textures (.jpg, .png) and additional binary
 		data (.bin). A glTF asset may deliver one or more scenes, including meshes, materials,
@@ -24,14 +24,14 @@
 
 		<p>
 			GLTFLoader supports the following
-			<a target="_blank" href="https://github.com/KhronosGroup/glTF/tree/master/extensions/">glTF 2.0 extensions</a>:
+			[link:https://github.com/KhronosGroup/glTF/tree/master/extensions/ glTF 2.0 extensions]:
 		</p>
 
 		<ul>
 			<li>KHR_draco_mesh_compression</li>
 			<li>KHR_materials_pbrSpecularGlossiness</li>
 			<li>KHR_materials_unlit</li>
-			<li>KHR_lights (experimental)</li>
+			<li>KHR_lights_punctual (experimental)</li>
 		</ul>
 
 		<h2>Example</h2>
@@ -79,9 +79,9 @@
 
 		<h2>Browser compatibility</h2>
 
-		<p>GLTFLoader relies on ES6 <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise">Promises</a>,
+		<p>GLTFLoader relies on ES6 [link:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise Promises],
 		which are not supported in IE11. To use the loader in IE11, you must
-		<a href="https://github.com/stefanpenner/es6-promise">include a polyfill</a>
+		[link:https://github.com/stefanpenner/es6-promise include a polyfill]
 		providing a Promise replacement.</p>
 
 		<h2>Custom extensions</h2>

+ 28 - 3
docs/examples/loaders/SVGLoader.html

@@ -12,7 +12,7 @@
 		<h1>[name]</h1>
 
 		<p class="desc">A loader for loading a <em>.svg</em> resource.<br >
-		<a href="https://en.wikipedia.org/wiki/Scalable_Vector_Graphics">Scalabe Vector Graphics</a> is an XML-based vector image format for two-dimensional graphics with support for interactivity and animation.
+		<a href="https://en.wikipedia.org/wiki/Scalable_Vector_Graphics">Scalable Vector Graphics</a> is an XML-based vector image format for two-dimensional graphics with support for interactivity and animation.
 		</p>
 
 		<h2>Example</h2>
@@ -26,9 +26,34 @@
 			// resource URL
 			'data/svgSample.svg',
 			// called when the resource is loaded
-			function ( doc ) {
+			function ( paths ) {
 
-				scene.add( new THREE.SVGObject(doc) );
+				var group = new THREE.Group();
+
+				for ( var i = 0; i < paths.length; i ++ ) {
+
+					var path = paths[ i ];
+
+					var material = new THREE.MeshBasicMaterial( {
+						color: path.color,
+						side: THREE.DoubleSide,
+						depthWrite: false
+					} );
+
+					var shapes = path.toShapes( true );
+
+					for ( var j = 0; j < shapes.length; j ++ ) {
+
+						var shape = shapes[ j ];
+						var geometry = new THREE.ShapeBufferGeometry( shape );
+						var mesh = new THREE.Mesh( geometry, material );
+						group.add( mesh );
+
+					}
+
+				}
+
+				scene.add( group );
 
 			},
 			// called when loading is in progresses

+ 1 - 1
docs/examples/renderers/SVGRenderer.html

@@ -21,7 +21,7 @@
 				<li>Complex or animated user interfaces</li>
 			</ul>
 			<p>
-				[name] has various advantages. It produces crystal-clear and sharp output which is independet of the actual viewport resolution.<br />
+				[name] has various advantages. It produces crystal-clear and sharp output which is independent of the actual viewport resolution.<br />
 				SVG elements can be styled via CSS. And they have good accessibility since it's possible to add metadata like title or description (useful for search engines or screen readers).
 			</p>
 			<p>

+ 2 - 0
docs/index.html

@@ -106,6 +106,8 @@
 				link.setAttribute( 'target', 'viewer' );
 				link.addEventListener( 'click', function ( event ) {
 
+					if ( event.button !== 0 || event.ctrlKey || event.altKey || event.metaKey ) return;
+
 					window.location.hash = pageURL;
 					panel.classList.add( 'collapsed' );
 

+ 6 - 10
docs/list.js

@@ -8,11 +8,11 @@ var list = {
 			"Browser support": "manual/introduction/Browser-support",
 			"WebGL compatibility check": "manual/introduction/WebGL-compatibility-check",
 			"How to run things locally": "manual/introduction/How-to-run-things-locally",
-			"Drawing Lines": "manual/introduction/Drawing-lines",
-			"Creating Text": "manual/introduction/Creating-text",
-			"Loading 3D Models": "manual/introduction/Loading-3D-models",
-			"Migration Guide": "manual/introduction/Migration-guide",
-			"Code Style Guide": "manual/introduction/Code-style-guide",
+			"Drawing lines": "manual/introduction/Drawing-lines",
+			"Creating text": "manual/introduction/Creating-text",
+			"Loading 3D models": "manual/introduction/Loading-3D-models",
+			"Migration guide": "manual/introduction/Migration-guide",
+			"Code style guide": "manual/introduction/Code-style-guide",
 			"FAQ": "manual/introduction/FAQ",
 			"Useful links": "manual/introduction/Useful-links"
 		},
@@ -20,7 +20,7 @@ var list = {
 		"Next Steps": {
 			"How to update things": "manual/introduction/How-to-update-things",
 			"Matrix transformations": "manual/introduction/Matrix-transformations",
-			"Animation System": "manual/introduction/Animation-system"
+			"Animation system": "manual/introduction/Animation-system"
 		},
 
 		"Build Tools": {
@@ -416,10 +416,6 @@ var list = {
 			"WebGLProgram": "api/renderers/webgl/WebGLProgram",
 			"WebGLShader": "api/renderers/webgl/WebGLShader",
 			"WebGLState": "api/renderers/webgl/WebGLState"
-		},
-
-		"WebGLRenderer / Plugins": {
-			"SpritePlugin": "api/renderers/webgl/plugins/SpritePlugin"
 		}
 
 	}

+ 6 - 6
editor/css/dark.css

@@ -53,7 +53,7 @@ select {
 	top: 32px;
 	left: 0;
 	right: 300px;
-	bottom: 32px;
+	bottom: 0;
 }
 
 	#viewport #info {
@@ -66,7 +66,7 @@ select {
 	top: 32px;
 	left: 0;
 	right: 300px;
-	bottom: 32px;
+	bottom: 0;
 	opacity: 0.9;
 }
 
@@ -75,7 +75,7 @@ select {
 	top: 32px;
 	left: 0;
 	right: 300px;
-	bottom: 32px;
+	bottom: 0;
 }
 
 #menubar {
@@ -201,9 +201,9 @@ select {
 
 #toolbar {
 	position: absolute;
-	left: 0;
-	right: 300px;
-	bottom: 0;
+	left: calc(50% - 290px); /* ( ( 100% - 300px ) / 2.0 ) - 140px */
+	width: 280px;
+	bottom: 16px;
 	height: 32px;
 	background-color: #111;
 	color: #333;

+ 6 - 6
editor/css/light.css

@@ -49,7 +49,7 @@ select {
 	top: 32px;
 	left: 0;
 	right: 300px;
-	bottom: 32px;
+	bottom: 0;
 }
 
 	#viewport #info {
@@ -62,7 +62,7 @@ select {
 	top: 32px;
 	left: 0;
 	right: 300px;
-	bottom: 32px;
+	bottom: 0;
 	opacity: 0.9;
 }
 
@@ -71,7 +71,7 @@ select {
 	top: 32px;
 	left: 0;
 	right: 300px;
-	bottom: 32px;
+	bottom: 0;
 }
 
 #menubar {
@@ -194,9 +194,9 @@ select {
 
 #toolbar {
 	position: absolute;
-	left: 0;
-	right: 300px;
-	bottom: 0;
+	left: calc(50% - 290px); /* ( ( 100% - 300px ) / 2.0 ) - 140px */
+	width: 280px;
+	bottom: 16px;
 	height: 32px;
 	background: #eee;
 	color: #333;

+ 134 - 63
editor/examples/arkanoid.app.json

@@ -3,65 +3,72 @@
 		"type": "App"
 	},
 	"project": {
+		"gammaInput": true,
+		"gammaOutput": true,
 		"shadows": true,
 		"vr": false
 	},
 	"camera": {
 		"metadata": {
-			"version": 4.4,
+			"version": 4.5,
 			"type": "Object",
 			"generator": "Object3D.toJSON"
 		},
 		"object": {
-			"uuid": "E41E9F54-8B31-4D1F-8D09-AF5E802E9A22",
+			"uuid": "0C0DD0AD-3A7F-4ECD-A9FE-CECD97D5CBD9",
 			"type": "PerspectiveCamera",
 			"name": "Camera",
-			"matrix": [0.9392361044883728,-2.8092810300250903e-9,-0.3432718515396118,0,-0.14778217673301697,0.902585506439209,-0.404351145029068,0,0.3098321855068207,0.43051064014434814,0.847740888595581,0,142.32125854492188,202.75485229492188,389.40936279296875,1],
+			"layers": 1,
+			"matrix": [0.939236,0,-0.343272,0,-0.147782,0.902586,-0.404351,0,0.309832,0.430511,0.847741,0,11.713146,19.228675,40.388679,1],
 			"fov": 50,
-			"aspect": 1.536388140161725,
+			"zoom": 1,
 			"near": 0.1,
-			"far": 100000
+			"far": 100000,
+			"focus": 10,
+			"aspect": 1.428977,
+			"filmGauge": 35,
+			"filmOffset": 0
 		}
 	},
 	"scene": {
 		"metadata": {
-			"version": 4.4,
+			"version": 4.5,
 			"type": "Object",
 			"generator": "Object3D.toJSON"
 		},
 		"geometries": [
 			{
-				"uuid": "8F05A1F2-3877-478B-8DFC-F572AC61AB3A",
+				"uuid": "BBEE74D1-E43D-4C32-A9F3-4656E78C26F3",
 				"type": "PlaneGeometry",
-				"width": 300,
-				"height": 400,
+				"width": 30,
+				"height": 40,
 				"widthSegments": 1,
 				"heightSegments": 1
 			},
 			{
-				"uuid": "EEDF0A9A-D174-44E4-9C2F-A2F5BB8BE7F5",
-				"type": "CylinderGeometry",
-				"radiusTop": 5,
-				"radiusBottom": 5,
-				"height": 20,
-				"radialSegments": 32,
-				"heightSegments": 1,
-				"openEnded": false
-			},
-			{
-				"uuid": "7149652B-DBD7-4CB7-A600-27A9AC005C95",
+				"uuid": "C1722F5F-89AD-45D8-B78C-D1D34AF2A012",
 				"type": "BoxGeometry",
-				"width": 20,
-				"height": 10,
-				"depth": 10,
+				"width": 2,
+				"height": 1,
+				"depth": 1,
 				"widthSegments": 1,
 				"heightSegments": 1,
 				"depthSegments": 1
 			},
 			{
-				"uuid": "CABCC711-1331-4D4C-9FF6-409299F10C68",
+				"uuid": "327EFFCF-649C-4EF3-86D4-B422C5A86E89",
+				"type": "CylinderGeometry",
+				"radiusTop": 0.5,
+				"radiusBottom": 0.5,
+				"height": 2,
+				"radialSegments": 32,
+				"heightSegments": 1,
+				"openEnded": false
+			},
+			{
+				"uuid": "0791211B-BB02-4E57-82B5-64C05DE92B39",
 				"type": "SphereGeometry",
-				"radius": 5,
+				"radius": 0.5,
 				"widthSegments": 32,
 				"heightSegments": 16,
 				"phiStart": 0,
@@ -70,14 +77,24 @@
 				"thetaLength": 3.14
 			},
 			{
-				"uuid": "EFBF641D-F092-462E-B7FB-0BFAD1591EFC",
+				"uuid": "73F12A47-9EA7-47FD-BCF3-89B8219B2626",
 				"type": "BoxGeometry",
-				"width": 20,
-				"height": 10,
-				"depth": 10,
+				"width": 2,
+				"height": 1,
+				"depth": 1,
 				"widthSegments": 1,
 				"heightSegments": 1,
 				"depthSegments": 1
+			},
+			{
+				"uuid": "3BDEB9FB-BDD4-44AD-8A47-008BED1C8982",
+				"type": "CylinderGeometry",
+				"radiusTop": 0.5,
+				"radiusBottom": 0.5,
+				"height": 2,
+				"radialSegments": 32,
+				"heightSegments": 1,
+				"openEnded": false
 			}],
 		"materials": [
 			{
@@ -86,7 +103,19 @@
 				"color": 86015,
 				"emissive": 0,
 				"specular": 1118481,
-				"shininess": 30
+				"shininess": 30,
+				"depthFunc": 3,
+				"depthTest": true,
+				"depthWrite": true
+			},
+			{
+				"uuid": "D98FC4D1-169E-420A-92EA-20E55009A46D",
+				"type": "MeshBasicMaterial",
+				"color": 63744,
+				"depthFunc": 3,
+				"depthTest": true,
+				"depthWrite": true,
+				"wireframe": true
 			},
 			{
 				"uuid": "3B9DE64D-E1C8-4C24-9F73-3A9E10E3E655",
@@ -94,13 +123,10 @@
 				"color": 16777215,
 				"emissive": 0,
 				"specular": 1118481,
-				"shininess": 30
-			},
-			{
-				"uuid": "D98FC4D1-169E-420A-92EA-20E55009A46D",
-				"type": "MeshBasicMaterial",
-				"wireframe": true,
-				"color": 63744
+				"shininess": 30,
+				"depthFunc": 3,
+				"depthTest": true,
+				"depthWrite": true
 			},
 			{
 				"uuid": "043B208C-1F83-42C6-802C-E0E35621C27C",
@@ -108,100 +134,145 @@
 				"color": 16777215,
 				"emissive": 0,
 				"specular": 1118481,
-				"shininess": 30
+				"shininess": 30,
+				"depthFunc": 3,
+				"depthTest": true,
+				"depthWrite": true
 			},
 			{
 				"uuid": "40EC9BDA-91C0-4671-937A-2BCB6DA7EEBB",
 				"type": "MeshBasicMaterial",
-				"wireframe": true,
-				"color": 63744
+				"color": 63744,
+				"depthFunc": 3,
+				"depthTest": true,
+				"depthWrite": true,
+				"wireframe": true
 			}],
 		"object": {
 			"uuid": "31517222-A9A7-4EAF-B5F6-60751C0BABA3",
 			"type": "Scene",
 			"name": "Scene",
+			"layers": 1,
 			"matrix": [1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1],
 			"children": [
 				{
 					"uuid": "EBBB1E63-6318-4752-AE2E-440A4E0B3EF3",
 					"type": "Mesh",
 					"name": "Ground",
-					"matrix": [1,0,0,0,0,0.0007960614748299122,-0.9999997019767761,0,0,0.9999997019767761,0.0007960614748299122,0,0,0,0,1],
-					"geometry": "8F05A1F2-3877-478B-8DFC-F572AC61AB3A",
+					"layers": 1,
+					"matrix": [1,0,0,0,0,0.000796,-1,0,0,1,0.000796,0,0,0,0,1],
+					"geometry": "BBEE74D1-E43D-4C32-A9F3-4656E78C26F3",
 					"material": "2F69AF3A-DDF5-4BBA-87B5-80159F90DDBF"
 				},
 				{
 					"uuid": "6EE2E764-43E0-48E0-85F2-E0C8823C20DC",
 					"type": "DirectionalLight",
 					"name": "DirectionalLight 1",
-					"matrix": [1,0,0,0,0,1,0,0,0,0,1,0,100,200,150,1],
+					"layers": 1,
+					"matrix": [1,0,0,0,0,1,0,0,0,0,1,0,10,20,15,1],
 					"color": 16777215,
-					"intensity": 1
+					"intensity": 1,
+					"shadow": {
+						"camera": {
+							"uuid": "3BC010F7-9766-4087-BA04-1D4FD7721ABA",
+							"type": "OrthographicCamera",
+							"layers": 1,
+							"zoom": 1,
+							"left": -5,
+							"right": 5,
+							"top": 5,
+							"bottom": -5,
+							"near": 0.5,
+							"far": 500
+						}
+					}
 				},
 				{
 					"uuid": "38219749-1E67-45F2-AB15-E64BA0940CAD",
 					"type": "Mesh",
 					"name": "Brick",
-					"matrix": [1,0,0,0,0,1,0,0,0,0,1,0,0,5,0,1],
+					"layers": 1,
+					"matrix": [1,0,0,0,0,1,0,0,0,0,1,0,0,0.5,0,1],
+					"geometry": "C1722F5F-89AD-45D8-B78C-D1D34AF2A012",
+					"material": "D98FC4D1-169E-420A-92EA-20E55009A46D",
 					"children": [
 						{
 							"uuid": "711A5955-8F17-4A8B-991A-7604D27E6FA0",
 							"type": "Mesh",
 							"name": "Cylinder",
-							"matrix": [0.0007962009985931218,0.0007962677045725286,0.9999995231628418,0,-0.9999997615814209,3.462185702574061e-7,0.0007962677045725286,0,2.210134084634774e-7,-0.9999997615814209,0.0007962008821777999,0,0,0,0,1],
-							"geometry": "EEDF0A9A-D174-44E4-9C2F-A2F5BB8BE7F5",
+							"layers": 1,
+							"matrix": [0.000795,0.000795,1,0,-1.000001,-0.000001,0.000795,0,0.000001,-1.000001,0.000795,0,0,0,0,1],
+							"geometry": "327EFFCF-649C-4EF3-86D4-B422C5A86E89",
 							"material": "3B9DE64D-E1C8-4C24-9F73-3A9E10E3E655"
-						}],
-					"geometry": "7149652B-DBD7-4CB7-A600-27A9AC005C95",
-					"material": "D98FC4D1-169E-420A-92EA-20E55009A46D"
+						}]
 				},
 				{
 					"uuid": "18FFA67C-F893-4E7A-8A76-8D996DEBE0C6",
 					"type": "Mesh",
 					"name": "Ball",
-					"matrix": [1,0,0,0,0,1,0,0,0,0,1,0,0,5,35.54999923706055,1],
-					"geometry": "CABCC711-1331-4D4C-9FF6-409299F10C68",
+					"layers": 1,
+					"matrix": [1,0,0,0,0,1,0,0,0,0,1,0,0,0.5,3.55,1],
+					"geometry": "0791211B-BB02-4E57-82B5-64C05DE92B39",
 					"material": "043B208C-1F83-42C6-802C-E0E35621C27C"
 				},
 				{
 					"uuid": "6D660D49-39B8-40C3-95F6-E4E007AA8D79",
 					"type": "Mesh",
 					"name": "Paddle",
-					"matrix": [2,0,0,0,0,1,0,0,0,0,1,0,0,5,159.5399932861328,1],
+					"layers": 1,
+					"matrix": [2,0,0,0,0,1,0,0,0,0,1,0,0,0.5,15.95,1],
+					"geometry": "73F12A47-9EA7-47FD-BCF3-89B8219B2626",
+					"material": "40EC9BDA-91C0-4671-937A-2BCB6DA7EEBB",
 					"children": [
 						{
 							"uuid": "4F5F884C-9E1B-45E6-8F1E-4D538A46D8CB",
 							"type": "Mesh",
 							"name": "Cylinder",
-							"matrix": [0.0007962009985931218,0.0007962677045725286,0.9999995231628418,0,-0.9999997615814209,3.462185702574061e-7,0.0007962677045725286,0,2.210134084634774e-7,-0.9999997615814209,0.0007962008821777999,0,0,0,0,1],
-							"geometry": "EEDF0A9A-D174-44E4-9C2F-A2F5BB8BE7F5",
+							"layers": 1,
+							"matrix": [0.000795,0.000795,1,0,-1.000001,-0.000001,0.000795,0,0.000001,-1.000001,0.000795,0,0,0,0,1],
+							"geometry": "3BDEB9FB-BDD4-44AD-8A47-008BED1C8982",
 							"material": "3B9DE64D-E1C8-4C24-9F73-3A9E10E3E655"
-						}],
-					"geometry": "EFBF641D-F092-462E-B7FB-0BFAD1591EFC",
-					"material": "40EC9BDA-91C0-4671-937A-2BCB6DA7EEBB"
+						}]
 				},
 				{
 					"uuid": "B0BEAF69-8B5D-4D87-ADCA-FDE83A02762D",
 					"type": "PointLight",
 					"name": "PointLight 2",
-					"matrix": [1,0,0,0,0,1,0,0,0,0,1,0,-116.54356384277344,69.48957061767578,-206.8248291015625,1],
+					"layers": 1,
+					"matrix": [1,0,0,0,0,1,0,0,0,0,1,0,-11.65,6.949,-20.682,1],
 					"color": 16777215,
 					"intensity": 1,
 					"distance": 0,
-					"decay": 1
-				}]
+					"decay": 1,
+					"shadow": {
+						"camera": {
+							"uuid": "2F0DA21A-EFB8-4E9A-83C5-A601D6113780",
+							"type": "PerspectiveCamera",
+							"layers": 1,
+							"fov": 90,
+							"zoom": 1,
+							"near": 0.5,
+							"far": 500,
+							"focus": 10,
+							"aspect": 1,
+							"filmGauge": 35,
+							"filmOffset": 0
+						}
+					}
+				}],
+			"background": 11184810
 		}
 	},
 	"scripts": {
 		"6D660D49-39B8-40C3-95F6-E4E007AA8D79": [
 			{
 				"name": "User",
-				"source": "function mousemove( event ) {\n\n\tthis.position.x = ( event.clientX / player.width ) * 300 - 150;\n\n}\n\n// function update( event ) {}"
+				"source": "function mousemove( event ) {\n\n\tthis.position.x = ( event.clientX / player.width ) * 30 - 15;\n\n}\n\n// function update( event ) {}"
 			}],
 		"31517222-A9A7-4EAF-B5F6-60751C0BABA3": [
 			{
 				"name": "Game Logic",
-				"source": "var ball = this.getObjectByName( 'Ball' );\n\nvar direction = new THREE.Vector3();\ndirection.x = Math.random() - 0.5;\ndirection.z = - 0.5;\ndirection.normalize();\n\nvar speed = new THREE.Vector3();\n\n//\n\nvar group = new THREE.Group();\nthis.add( group );\n\nvar paddle = this.getObjectByName( 'Paddle' );\npaddle.material.visible = false;\ngroup.add( paddle );\n\nvar brick = this.getObjectByName( 'Brick' );\n\nfor ( var j = 0; j < 8; j ++ ) {\n\n\tvar material = new THREE.MeshPhongMaterial( { color: Math.random() * 0xffffff } );\n\n\tfor ( var i = 0; i < 12; i ++ ) {\n\t\t\n\t\tvar object = brick.clone();\n\t\tobject.position.x = i * 22 - 120;\n\t\tobject.position.z = j * 14 - 120;\n\t\tgroup.add( object );\n\n\t\tvar cylinder = object.getObjectByName( 'Cylinder' );\n\t\tcylinder.material = material;\n\n\t}\n\t\n}\n\nbrick.visible = false;\nbrick.material.visible = false;\n\n//\n\nvar raycaster = new THREE.Raycaster();\n\nfunction update( event ) {\n\t\n\tif ( ball.position.x < - 150 || ball.position.x > 150 ) direction.x = - direction.x;\n\tif ( ball.position.z < - 200 || ball.position.z > 200 ) direction.z = - direction.z;\n\n\tball.position.x = Math.max( - 150, Math.min( 150, ball.position.x ) );\n\tball.position.z = Math.max( - 200, Math.min( 200, ball.position.z ) );\n\t\n\tball.position.add( speed.copy( direction ).multiplyScalar( event.delta / 4 ) );\n\t\n\traycaster.set( ball.position, direction );\n\t\n\tvar intersections = raycaster.intersectObjects( group.children );\n\t\n\tif ( intersections.length > 0 ) {\n\t\n\t\tvar intersection = intersections[ 0 ];\n\t\t\n\t\tif ( intersection.distance < 5 ) {\n\t\t\t\n\t\t\tif ( intersection.object !== paddle ) {\n\n\t\t\t\tgroup.remove( intersection.object );\n\t\t\t\t\n\t\t\t}\n\t\t\t\n\t\t\tdirection.reflect( intersection.face.normal );\n\t\t\t\n\t\t}\n\t\t\n\t}\n\n}"
+				"source": "var ball = this.getObjectByName( 'Ball' );\n\nvar direction = new THREE.Vector3();\ndirection.x = Math.random() - 0.5;\ndirection.z = - 0.5;\ndirection.normalize();\n\nvar speed = new THREE.Vector3();\n\n//\n\nvar group = new THREE.Group();\nthis.add( group );\n\nvar paddle = this.getObjectByName( 'Paddle' );\npaddle.material.visible = false;\ngroup.add( paddle );\n\nvar brick = this.getObjectByName( 'Brick' );\n\nfor ( var j = 0; j < 8; j ++ ) {\n\n\tvar material = new THREE.MeshPhongMaterial( { color: Math.random() * 0xffffff } );\n\n\tfor ( var i = 0; i < 12; i ++ ) {\n\t\t\n\t\tvar object = brick.clone();\n\t\tobject.position.x = i * 2.2 - 12;\n\t\tobject.position.z = j * 1.4 - 12;\n\t\tgroup.add( object );\n\n\t\tvar cylinder = object.getObjectByName( 'Cylinder' );\n\t\tcylinder.material = material;\n\n\t}\n\t\n}\n\nbrick.visible = false;\nbrick.material.visible = false;\n\n//\n\nvar raycaster = new THREE.Raycaster();\n\nfunction update( event ) {\n\t\n\tif ( ball.position.x < - 15 || ball.position.x > 15 ) direction.x = - direction.x;\n\tif ( ball.position.z < - 20 || ball.position.z > 20 ) direction.z = - direction.z;\n\n\tball.position.x = Math.max( - 15, Math.min( 15, ball.position.x ) );\n\tball.position.z = Math.max( - 20, Math.min( 20, ball.position.z ) );\n\t\n\tball.position.add( speed.copy( direction ).multiplyScalar( event.delta / 40 ) );\n\t\n\traycaster.set( ball.position, direction );\n\t\n\tvar intersections = raycaster.intersectObjects( group.children );\n\t\n\tif ( intersections.length > 0 ) {\n\t\n\t\tvar intersection = intersections[ 0 ];\n\t\t\n\t\tif ( intersection.distance < 0.5 ) {\n\t\t\t\n\t\t\tif ( intersection.object !== paddle ) {\n\n\t\t\t\tgroup.remove( intersection.object );\n\t\t\t\t\n\t\t\t}\n\t\t\t\n\t\t\tdirection.reflect( intersection.face.normal );\n\t\t\t\n\t\t}\n\t\t\n\t}\n\n}"
 			}]
 	}
 }

+ 106 - 43
editor/examples/camera.app.json

@@ -3,65 +3,72 @@
 		"type": "App"
 	},
 	"project": {
+		"gammaInput": true,
+		"gammaOutput": true,
 		"shadows": true,
 		"vr": false
 	},
 	"camera": {
 		"metadata": {
-			"version": 4.3,
+			"version": 4.5,
 			"type": "Object",
-			"generator": "ObjectExporter"
+			"generator": "Object3D.toJSON"
 		},
 		"object": {
-			"uuid": "C7FB195B-270E-47B4-95C9-1754652A9D11",
+			"uuid": "60EBAF60-53DA-47B0-A028-8FC031B708F6",
 			"type": "PerspectiveCamera",
 			"name": "Camera",
+			"layers": 1,
+			"matrix": [0.970041,0,-0.242943,0,-0.048226,0.980099,-0.192562,0,0.238108,0.198509,0.950736,0,1.548,1.29,6.18,1],
 			"fov": 50,
-			"aspect": 1.2252042007001167,
+			"zoom": 1,
 			"near": 0.1,
 			"far": 100000,
-			"matrix": [0.9700406789779663,-2.851828329042405e-9,-0.24294254183769226,0,-0.04822639003396034,0.9800989627838135,-0.1925622522830963,0,0.23810774087905884,0.19850945472717285,0.950735867023468,0,154.7735595703125,129.03408813476562,617.992431640625,1]
+			"focus": 10,
+			"aspect": 1.428977,
+			"filmGauge": 35,
+			"filmOffset": 0
 		}
 	},
 	"scene": {
 		"metadata": {
-			"version": 4.3,
+			"version": 4.5,
 			"type": "Object",
-			"generator": "ObjectExporter"
+			"generator": "Object3D.toJSON"
 		},
 		"geometries": [
 			{
-				"uuid": "51BB3E54-D2DF-4576-9953-FB8E940588B5",
+				"uuid": "6D90C4BE-EBA6-4E21-8F54-7CFDAA61F30B",
 				"type": "PlaneGeometry",
-				"width": 1000,
-				"height": 1000,
+				"width": 10,
+				"height": 10,
 				"widthSegments": 1,
 				"heightSegments": 1
 			},
 			{
-				"uuid": "D8E200D3-27BC-49F8-A5C5-7384206E70FE",
+				"uuid": "D3008B2A-ACDD-43CC-87F7-4F942607D21A",
 				"type": "BoxGeometry",
-				"width": 100,
-				"height": 100,
-				"depth": 100,
+				"width": 1,
+				"height": 1,
+				"depth": 1,
 				"widthSegments": 1,
 				"heightSegments": 1,
 				"depthSegments": 1
 			},
 			{
-				"uuid": "25BA32DB-8B02-4ABA-A77C-69868C464A1A",
+				"uuid": "F482ACD4-013A-49CF-AE0F-C9FF4ADAE409",
 				"type": "CylinderGeometry",
 				"radiusTop": 0,
-				"radiusBottom": 40,
-				"height": 75,
+				"radiusBottom": 0.4,
+				"height": 0.75,
 				"radialSegments": 4,
 				"heightSegments": 1,
 				"openEnded": false
 			},
 			{
-				"uuid": "4DECFAB5-6FD1-4D84-9A29-565807B074EA",
+				"uuid": "51CDDCED-BC71-4B1B-A485-725B6A48204B",
 				"type": "IcosahedronGeometry",
-				"radius": 40,
+				"radius": 0.4,
 				"detail": 2
 			}],
 		"materials": [
@@ -71,7 +78,10 @@
 				"color": 16777215,
 				"emissive": 0,
 				"specular": 1118481,
-				"shininess": 30
+				"shininess": 30,
+				"depthFunc": 3,
+				"depthTest": true,
+				"depthWrite": true
 			},
 			{
 				"uuid": "B5943856-E404-45D9-A427-4774202C2CD0",
@@ -79,7 +89,10 @@
 				"color": 37119,
 				"emissive": 0,
 				"specular": 1118481,
-				"shininess": 30
+				"shininess": 30,
+				"depthFunc": 3,
+				"depthTest": true,
+				"depthWrite": true
 			},
 			{
 				"uuid": "3F872310-2067-4BE4-9250-5B3F4E43797E",
@@ -87,7 +100,10 @@
 				"color": 15859456,
 				"emissive": 0,
 				"specular": 1118481,
-				"shininess": 30
+				"shininess": 30,
+				"depthFunc": 3,
+				"depthTest": true,
+				"depthWrite": true
 			},
 			{
 				"uuid": "E1826901-7922-4584-A25D-6D487E2C9BBD",
@@ -95,83 +111,130 @@
 				"color": 16711680,
 				"emissive": 0,
 				"specular": 1118481,
-				"shininess": 30
+				"shininess": 30,
+				"depthFunc": 3,
+				"depthTest": true,
+				"depthWrite": true
 			}],
 		"object": {
 			"uuid": "3741222A-BD8F-401C-A5D2-5A907E891896",
 			"type": "Scene",
 			"name": "Scene",
+			"layers": 1,
 			"matrix": [1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1],
 			"children": [
 				{
 					"uuid": "B7CBBC6F-EC26-49B5-8D0D-67D9C535924B",
 					"type": "Group",
 					"name": "Dummy",
-					"matrix": [1,0,0,0,0,1,0,0,0,0,1,0,0,100,400,1],
+					"layers": 1,
+					"matrix": [1,0,0,0,0,1,0,0,0,0,1,0,0,1,4,1],
 					"children": [
 						{
 							"uuid": "60B69C58-4201-43FD-815E-AD2EDFBBD0CE",
 							"type": "PerspectiveCamera",
 							"name": "PerspectiveCamera",
+							"layers": 1,
+							"matrix": [-1,0,0,0,0,1,0,0,0,0,-1,0,0,0,0,1],
 							"fov": 50,
+							"zoom": 1,
+							"near": 0.1,
+							"far": 100,
+							"focus": 10,
 							"aspect": 1,
-							"near": 100,
-							"far": 10000,
-							"matrix": [-1,0,-1.2246468525851679e-16,0,0,1,0,0,1.2246468525851679e-16,0,-1,0,0,0,0,1]
+							"filmGauge": 35,
+							"filmOffset": 0
 						}]
 				},
 				{
 					"uuid": "A460C230-DC88-4A8F-A3FB-AA0FE735F3ED",
 					"type": "Mesh",
 					"name": "Plane",
-					"geometry": "51BB3E54-D2DF-4576-9953-FB8E940588B5",
-					"material": "4AE8130E-B6A8-47BC-ACCF-060973C74044",
-					"matrix": [1,0,0,0,0,0.040785226970911026,-0.9991679191589355,0,0,0.9991679191589355,0.040785226970911026,0,0,-50,0,1]
+					"layers": 1,
+					"matrix": [1,0,0,0,0,0.040785,-0.999168,0,0,0.999168,0.040785,0,0,-0.5,0,1],
+					"geometry": "6D90C4BE-EBA6-4E21-8F54-7CFDAA61F30B",
+					"material": "4AE8130E-B6A8-47BC-ACCF-060973C74044"
 				},
 				{
 					"uuid": "26DAAD69-725D-43B7-AF9D-990A99DEF8C5",
 					"type": "Mesh",
 					"name": "Box",
-					"geometry": "D8E200D3-27BC-49F8-A5C5-7384206E70FE",
-					"material": "B5943856-E404-45D9-A427-4774202C2CD0",
-					"matrix": [1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]
+					"layers": 1,
+					"matrix": [1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1],
+					"geometry": "D3008B2A-ACDD-43CC-87F7-4F942607D21A",
+					"material": "B5943856-E404-45D9-A427-4774202C2CD0"
 				},
 				{
 					"uuid": "AAAFF2D6-4725-4AFC-A9FE-26419B11011F",
 					"type": "Mesh",
 					"name": "Cylinder",
-					"geometry": "25BA32DB-8B02-4ABA-A77C-69868C464A1A",
-					"material": "3F872310-2067-4BE4-9250-5B3F4E43797E",
-					"matrix": [1,0,0,0,0,1,0,0,0,0,1,0,-130,-15,0,1]
+					"layers": 1,
+					"matrix": [1,0,0,0,0,1,0,0,0,0,1,0,-1.3,-0.15,0,1],
+					"geometry": "F482ACD4-013A-49CF-AE0F-C9FF4ADAE409",
+					"material": "3F872310-2067-4BE4-9250-5B3F4E43797E"
 				},
 				{
 					"uuid": "B855E267-A266-4098-ACD6-6A1FDE7B88BA",
 					"type": "Mesh",
 					"name": "Icosahedron",
-					"geometry": "4DECFAB5-6FD1-4D84-9A29-565807B074EA",
-					"material": "E1826901-7922-4584-A25D-6D487E2C9BBD",
-					"matrix": [1,0,0,0,0,1,0,0,0,0,1,0,130,-10,0,1]
+					"layers": 1,
+					"matrix": [1,0,0,0,0,1,0,0,0,0,1,0,1.3,-0.1,0,1],
+					"geometry": "51CDDCED-BC71-4B1B-A485-725B6A48204B",
+					"material": "E1826901-7922-4584-A25D-6D487E2C9BBD"
 				},
 				{
 					"uuid": "E2939A7B-5E40-438A-8C1B-32126FBC6892",
 					"type": "PointLight",
 					"name": "PointLight 1",
+					"layers": 1,
+					"matrix": [1,0,0,0,0,1,0,0,0,0,1,0,-0.939,1.271,-1.143,1],
 					"color": 9474221,
 					"intensity": 0.75,
 					"distance": 0,
 					"decay": 1,
-					"matrix": [1,0,0,0,0,1,0,0,0,0,1,0,-93.86000061035156,127.12999725341797,-114.30000305175781,1]
+					"shadow": {
+						"camera": {
+							"uuid": "EFF42F46-1E27-4B36-B9D9-CF7D879D258E",
+							"type": "PerspectiveCamera",
+							"layers": 1,
+							"fov": 90,
+							"zoom": 1,
+							"near": 0.5,
+							"far": 500,
+							"focus": 10,
+							"aspect": 1,
+							"filmGauge": 35,
+							"filmOffset": 0
+						}
+					}
 				},
 				{
 					"uuid": "3412781E-27CC-43C3-A5DB-54C0C8E42ED6",
 					"type": "PointLight",
 					"name": "PointLight 2",
+					"layers": 1,
+					"matrix": [1,0,0,0,0,1,0,0,0,0,1,0,0.881,0.083,1.254,1],
 					"color": 12773063,
 					"intensity": 1,
 					"distance": 0,
 					"decay": 1,
-					"matrix": [1,0,0,0,0,1,0,0,0,0,1,0,88.12999725341797,8.3100004196167,125.44999694824219,1]
-				}]
+					"shadow": {
+						"camera": {
+							"uuid": "81E800FE-E8A7-4A9E-AFAA-4F04FD56AFE4",
+							"type": "PerspectiveCamera",
+							"layers": 1,
+							"fov": 90,
+							"zoom": 1,
+							"near": 0.5,
+							"far": 500,
+							"focus": 10,
+							"aspect": 1,
+							"filmGauge": 35,
+							"filmOffset": 0
+						}
+					}
+				}],
+			"background": 11184810
 		}
 	},
 	"scripts": {
@@ -183,7 +246,7 @@
 		"B7CBBC6F-EC26-49B5-8D0D-67D9C535924B": [
 			{
 				"name": "Orbit",
-				"source": "function update( event ) {\n\n\tvar time = event.time * 0.001;\n\n\tthis.position.x = Math.sin( time ) * 400;\n\tthis.position.z = Math.cos( time ) * 400;\n\tthis.lookAt( scene.position );\n\n}"
+				"source": "function update( event ) {\n\n\tvar time = event.time * 0.001;\n\n\tthis.position.x = Math.sin( time ) * 4;\n\tthis.position.z = Math.cos( time ) * 4;\n\tthis.lookAt( scene.position );\n\n}"
 			}]
 	}
 }

+ 53 - 17
editor/examples/particles.app.json

@@ -3,42 +3,45 @@
 		"type": "App"
 	},
 	"project": {
+		"gammaInput": true,
+		"gammaOutput": true,
 		"shadows": true,
 		"vr": false
 	},
 	"camera": {
 		"metadata": {
-			"version": 4.4,
+			"version": 4.5,
 			"type": "Object",
 			"generator": "Object3D.toJSON"
 		},
 		"object": {
-			"uuid": "763F3000-7D7C-4BE6-80B6-914DEEBD9AA2",
+			"uuid": "056199EB-6985-481B-97CC-A57FB7C87809",
 			"type": "PerspectiveCamera",
 			"name": "Camera",
-			"matrix": [0.7071067690849304,-3.398992198810902e-11,-0.7071068286895752,0,-0.2357022613286972,0.9428090453147888,-0.235702246427536,0,0.6666666865348816,0.3333333134651184,0.6666666269302368,0,41.824005126953125,20.912002563476562,41.824005126953125,1],
+			"layers": 1,
+			"matrix": [0.707107,0,-0.707107,0,-0.235702,0.942809,-0.235702,0,0.666667,0.333333,0.666667,0,4.182,2.091,4.182,1],
 			"fov": 50,
 			"zoom": 1,
 			"near": 0.1,
 			"far": 100000,
 			"focus": 10,
-			"aspect": 0.46657381615598886,
+			"aspect": 0.666193,
 			"filmGauge": 35,
 			"filmOffset": 0
 		}
 	},
 	"scene": {
 		"metadata": {
-			"version": 4.4,
+			"version": 4.5,
 			"type": "Object",
 			"generator": "Object3D.toJSON"
 		},
 		"geometries": [
 			{
-				"uuid": "E80D9EC5-D722-4812-8226-5F355EAC9B96",
+				"uuid": "C3C0CE7D-10B8-43FC-8F74-011CC6E57800",
 				"type": "PlaneGeometry",
-				"width": 1000,
-				"height": 1000,
+				"width": 100,
+				"height": 100,
 				"widthSegments": 1,
 				"heightSegments": 1
 			}],
@@ -49,51 +52,84 @@
 				"color": 5465019,
 				"roughness": 1,
 				"metalness": 0,
-				"emissive": 0
+				"emissive": 0,
+				"depthFunc": 3,
+				"depthTest": true,
+				"depthWrite": true
 			},
 			{
 				"uuid": "F5361474-F5F1-412F-8D99-3699B868092D",
 				"type": "SpriteMaterial",
-				"color": 16777215
+				"color": 16777215,
+				"transparent": true,
+				"depthFunc": 3,
+				"depthTest": true,
+				"depthWrite": true
 			}],
 		"object": {
 			"uuid": "3741222A-BD8F-401C-A5D2-5A907E891896",
 			"type": "Scene",
 			"name": "Scene",
+			"layers": 1,
 			"matrix": [1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1],
 			"children": [
 				{
 					"uuid": "05B57416-1BE5-4A96-BB05-9D9CD112D52B",
 					"type": "Mesh",
 					"name": "Ground",
-					"matrix": [1,0,0,0,0,0.0007959486683830619,-0.9999997019767761,0,0,0.9999997019767761,0.0007959486683830619,0,0,-0.5,0,1],
-					"geometry": "E80D9EC5-D722-4812-8226-5F355EAC9B96",
+					"layers": 1,
+					"matrix": [1,0,0,0,0,0.000796,-1,0,0,1,0.000796,0,0,-0.5,0,1],
+					"geometry": "C3C0CE7D-10B8-43FC-8F74-011CC6E57800",
 					"material": "3A9449D2-62DB-4BB4-ABBD-6F3F9D46DE1A"
 				},
 				{
 					"uuid": "0A3CB873-07E6-4EEB-830B-68192504111B",
 					"type": "Sprite",
 					"name": "Particle",
-					"matrix": [0.4000000059604645,0,0,0,0,0.4000000059604645,0,0,0,0,0.4000000059604645,0,0,0,0,1],
+					"layers": 1,
+					"matrix": [0.04,0,0,0,0,0.04,0,0,0,0,0.04,0,0,0,0,1],
 					"material": "F5361474-F5F1-412F-8D99-3699B868092D"
 				},
 				{
 					"uuid": "40E5CDA4-0E39-4265-9293-3E9EC3207F61",
 					"type": "PointLight",
 					"name": "PointLight",
-					"matrix": [1,0,0,0,0,1,0,0,0,0,1,0,0,11.828879356384277,0,1],
+					"layers": 1,
+					"matrix": [1,0,0,0,0,1,0,0,0,0,1,0,0,1.183,0,1],
 					"color": 16777215,
 					"intensity": 1,
 					"distance": 0,
-					"decay": 1
-				}]
+					"decay": 1,
+					"shadow": {
+						"camera": {
+							"uuid": "B6D3493E-E5C9-4D65-9E26-BB788D127BE1",
+							"type": "PerspectiveCamera",
+							"layers": 1,
+							"fov": 90,
+							"zoom": 1,
+							"near": 0.5,
+							"far": 500,
+							"focus": 10,
+							"aspect": 1,
+							"filmGauge": 35,
+							"filmOffset": 0
+						}
+					}
+				}],
+			"background": 2171689,
+			"fog": {
+				"type": "Fog",
+				"color": 2171688,
+				"near": 1,
+				"far": 50
+			}
 		}
 	},
 	"scripts": {
 		"3741222A-BD8F-401C-A5D2-5A907E891896": [
 			{
 				"name": "Fountain",
-				"source": "var original = this.getObjectByName( 'Particle' );\n\nvar particles = [];\n\nfor ( var i = 0; i < 100; i ++ ) {\n\n\tvar particle = original.clone();\n\tparticle.userData.velocity = new THREE.Vector3();\n\tthis.add( particle );\n\n\tparticles.push( particle );\n\n}\n\nfunction update( event ) {\n\t\n\tvar particle = particles.shift();\n\tparticles.push( particle );\n\t\t\n\tvar velocity = particle.userData.velocity;\n\tvelocity.x = Math.random() - 0.5;\n\tvelocity.y = Math.random() + 1;\n\tvelocity.z = Math.random() - 0.5;\n\n\tfor ( var i = 0; i < particles.length; i ++ ) {\n\n\t\tvar particle = particles[ i ];\n\n\t\tvar velocity = particle.userData.velocity;\n\n\t\tvelocity.y -= 0.098;\n\n\t\tparticle.position.add( velocity );\n\n\t\tif ( particle.position.y < 0 ) {\n\n\t\t\tparticle.position.y = 0;\n\n\t\t\tvelocity.y = - velocity.y;\n\t\t\tvelocity.multiplyScalar( 0.6 );\n\n\t\t}\n\n\t}\n\n}"
+				"source": "var original = this.getObjectByName( 'Particle' );\n\nvar particles = [];\n\nfor ( var i = 0; i < 100; i ++ ) {\n\n\tvar particle = original.clone();\n\tparticle.userData.velocity = new THREE.Vector3();\n\tthis.add( particle );\n\n\tparticles.push( particle );\n\n}\n\nfunction update( event ) {\n\t\n\tvar particle = particles.shift();\n\tparticles.push( particle );\n\t\t\n\tvar velocity = particle.userData.velocity;\n\tvelocity.x = Math.random() * 0.1 - 0.05;\n\tvelocity.y = Math.random() * 0.1 + 0.1;\n\tvelocity.z = Math.random() * 0.1 - 0.05;\n\n\tfor ( var i = 0; i < particles.length; i ++ ) {\n\n\t\tvar particle = particles[ i ];\n\n\t\tvar velocity = particle.userData.velocity;\n\n\t\tvelocity.y -= 0.0098;\n\n\t\tparticle.position.add( velocity );\n\n\t\tif ( particle.position.y < 0 ) {\n\n\t\t\tparticle.position.y = 0;\n\n\t\t\tvelocity.y = - velocity.y;\n\t\t\tvelocity.multiplyScalar( 0.6 );\n\n\t\t}\n\n\t}\n\n}"
 			}]
 	}
 }

+ 74 - 39
editor/examples/pong.app.json

@@ -3,69 +3,74 @@
 		"type": "App"
 	},
 	"project": {
-		"shadows": false,
+		"gammaInput": true,
+		"gammaOutput": true,
+		"shadows": true,
 		"vr": false
 	},
 	"camera": {
 		"metadata": {
-			"version": 4.4,
+			"version": 4.5,
 			"type": "Object",
 			"generator": "Object3D.toJSON"
 		},
 		"object": {
-			"uuid": "B901700E-2B1B-4D74-9201-164193F8304E",
+			"uuid": "4AC7ADED-CC22-4B16-8218-2E0A0C38C8F8",
 			"type": "PerspectiveCamera",
 			"name": "Camera",
-			"matrix": [0.9522120356559753,4.608077919243669e-9,-0.3054378032684326,0,-0.17742955684661865,0.8139732480049133,-0.553142249584198,0,0.24861818552017212,0.5809023976325989,0.7750750780105591,0,18.645999908447266,43.56800079345703,58.13100051879883,1],
-			"focalLength": 10,
-			"zoom": 1,
+			"layers": 1,
+			"matrix": [0.952212,0,-0.305438,0,-0.17743,0.813973,-0.553142,0,0.248618,0.580902,0.775075,0,1.865,4.357,5.813,1],
 			"fov": 50,
-			"aspect": 1.3217270194986073,
+			"zoom": 1,
 			"near": 0.1,
-			"far": 100000
+			"far": 100000,
+			"focus": 10,
+			"aspect": 1.428977,
+			"filmGauge": 35,
+			"filmOffset": 0
 		}
 	},
 	"scene": {
 		"metadata": {
-			"version": 4.4,
+			"version": 4.5,
 			"type": "Object",
 			"generator": "Object3D.toJSON"
 		},
 		"geometries": [
 			{
-				"uuid": "713F75F5-5D04-4069-89CB-2035F5619AC3",
+				"uuid": "490CEBA3-6A25-4BE1-B517-C5FB11A5D18A",
 				"type": "PlaneGeometry",
-				"width": 60,
-				"height": 40,
+				"width": 6,
+				"height": 4,
 				"widthSegments": 1,
 				"heightSegments": 1
 			},
 			{
-				"uuid": "4537EA66-3CD6-43A1-97A9-EB59F3258BF9",
+				"uuid": "D9A92F2D-2F08-4851-99C7-12D8D1CA13C7",
 				"type": "BoxGeometry",
-				"width": 1,
-				"height": 1,
-				"depth": 1,
+				"width": 0.1,
+				"height": 0.1,
+				"depth": 0.1,
 				"widthSegments": 1,
 				"heightSegments": 1,
 				"depthSegments": 1
 			},
 			{
-				"uuid": "3C546CA4-FF0F-4BA1-9406-0CD0D560A396",
+				"uuid": "5E63B8CF-E225-4ABC-994A-4D06BD4E21EB",
 				"type": "BoxGeometry",
-				"width": 2,
-				"height": 2,
-				"depth": 10,
+				"width": 0.2,
+				"height": 0.2,
+				"depth": 1,
 				"widthSegments": 1,
 				"heightSegments": 1,
 				"depthSegments": 1
 			},
 			{
-				"uuid": "4628F4A7-D572-45C2-9A67-807D71FF19EC",
+				"uuid": "D61532B4-24C3-4BC4-B56B-7245E8163E09",
 				"type": "BoxGeometry",
-				"width": 2,
-				"height": 2,
-				"depth": 10,
+				"width": 0.2,
+				"height": 0.2,
+				"depth": 1,
 				"widthSegments": 1,
 				"heightSegments": 1,
 				"depthSegments": 1
@@ -77,7 +82,10 @@
 				"color": 16777215,
 				"emissive": 0,
 				"specular": 16777215,
-				"shininess": 30
+				"shininess": 30,
+				"depthFunc": 3,
+				"depthTest": true,
+				"depthWrite": true
 			},
 			{
 				"uuid": "B1CAF098-FE36-45E1-BEBE-8D6AC04821CC",
@@ -85,7 +93,10 @@
 				"color": 16711680,
 				"emissive": 0,
 				"specular": 1118481,
-				"shininess": 30
+				"shininess": 30,
+				"depthFunc": 3,
+				"depthTest": true,
+				"depthWrite": true
 			},
 			{
 				"uuid": "FBDBE66D-B613-4741-802D-5AE1DE07DE46",
@@ -93,61 +104,85 @@
 				"color": 2752767,
 				"emissive": 0,
 				"specular": 1118481,
-				"shininess": 30
+				"shininess": 30,
+				"depthFunc": 3,
+				"depthTest": true,
+				"depthWrite": true
 			}],
 		"object": {
 			"uuid": "31517222-A9A7-4EAF-B5F6-60751C0BABA3",
 			"type": "Scene",
 			"name": "Scene",
+			"layers": 1,
 			"matrix": [1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1],
 			"children": [
 				{
 					"uuid": "B47D0BFC-D63A-4CBB-985E-9C4DBDF086E4",
 					"type": "Mesh",
 					"name": "Ground",
-					"matrix": [1,0,0,0,0,0.0007960238144733012,-0.9999997019767761,0,0,0.9999997019767761,0.0007960238144733012,0,0,-1,0,1],
-					"geometry": "713F75F5-5D04-4069-89CB-2035F5619AC3",
+					"layers": 1,
+					"matrix": [1,0,0,0,0,0.000796,-1,0,0,1,0.000796,0,0,-0.1,0,1],
+					"geometry": "490CEBA3-6A25-4BE1-B517-C5FB11A5D18A",
 					"material": "7EDF7C08-6325-418A-BBAB-89341C694730"
 				},
 				{
 					"uuid": "CE13E58A-4E8B-4F72-9E2E-7DE57C58F989",
 					"type": "Mesh",
 					"name": "Ball",
+					"layers": 1,
 					"matrix": [1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1],
-					"geometry": "4537EA66-3CD6-43A1-97A9-EB59F3258BF9",
+					"geometry": "D9A92F2D-2F08-4851-99C7-12D8D1CA13C7",
 					"material": "B1CAF098-FE36-45E1-BEBE-8D6AC04821CC"
 				},
 				{
 					"uuid": "2AAEA3AA-EC45-492B-B450-10473D1EC6C5",
 					"type": "Mesh",
 					"name": "Pad 1",
-					"matrix": [1,0,0,0,0,1,0,0,0,0,1,0,-24,0,0,1],
-					"geometry": "3C546CA4-FF0F-4BA1-9406-0CD0D560A396",
+					"layers": 1,
+					"matrix": [1,0,0,0,0,1,0,0,0,0,1,0,-2.4,0,0,1],
+					"geometry": "5E63B8CF-E225-4ABC-994A-4D06BD4E21EB",
 					"material": "FBDBE66D-B613-4741-802D-5AE1DE07DE46"
 				},
 				{
 					"uuid": "F1DD46A7-6584-4A37-BC76-852C3911077E",
 					"type": "Mesh",
 					"name": "Pad 2",
-					"matrix": [1,0,0,0,0,1,0,0,0,0,1,0,24,0,0,1],
-					"geometry": "4628F4A7-D572-45C2-9A67-807D71FF19EC",
+					"layers": 1,
+					"matrix": [1,0,0,0,0,1,0,0,0,0,1,0,2.4,0,0,1],
+					"geometry": "D61532B4-24C3-4BC4-B56B-7245E8163E09",
 					"material": "FBDBE66D-B613-4741-802D-5AE1DE07DE46"
 				},
 				{
 					"uuid": "C62AAE9F-9E51-46A5-BD2B-71BA804FC0B3",
 					"type": "DirectionalLight",
-					"name": "DirectionalLight 3",
-					"matrix": [1,0,0,0,0,1,0,0,0,0,1,0,10,20,15,1],
+					"name": "DirectionalLight",
+					"layers": 1,
+					"matrix": [1,0,0,0,0,1,0,0,0,0,1,0,1,2,1.5,1],
 					"color": 16777215,
-					"intensity": 1
-				}]
+					"intensity": 1,
+					"shadow": {
+						"camera": {
+							"uuid": "2CF1F42A-8992-4E8D-8D94-7CC20979344C",
+							"type": "OrthographicCamera",
+							"layers": 1,
+							"zoom": 1,
+							"left": -5,
+							"right": 5,
+							"top": 5,
+							"bottom": -5,
+							"near": 0.5,
+							"far": 500
+						}
+					}
+				}],
+			"background": 11184810
 		}
 	},
 	"scripts": {
 		"31517222-A9A7-4EAF-B5F6-60751C0BABA3": [
 			{
 				"name": "Game logic",
-				"source": "var ball = this.getObjectByName( 'Ball' );\n\nvar position = ball.position;\n\nvar velocity = new THREE.Vector3();\n\nvar direction = new THREE.Vector3();\ndirection.x = Math.random() - 0.5;\ndirection.z = Math.random() - 0.5;\ndirection.normalize();\n\nvar pad1 = this.getObjectByName( 'Pad 1' );\nvar pad2 = this.getObjectByName( 'Pad 2' );\n\nvar raycaster = new THREE.Raycaster();\nvar objects = [ pad1, pad2 ];\n\n//\n\nfunction mousemove( event ) {\n\n\tpad1.position.z = ( event.clientX / player.width ) * 30 - 15;\n\tpad2.position.z = - pad1.position.z;\n\n}\n\nfunction update( event ) {\n\t\n\tif ( position.x < -30 || position.x > 30 ) direction.x = - direction.x;\n\tif ( position.z < -20 || position.z > 20 ) direction.z = - direction.z;\n\t\n\tposition.x = Math.max( - 30, Math.min( 30, position.x ) );\n\tposition.z = Math.max( - 20, Math.min( 20, position.z ) );\n\t\n\traycaster.set( position, direction );\n\t\n\tvar intersections = raycaster.intersectObjects( objects );\n\t\n\tif ( intersections.length > 0 ) {\n\n\t\tvar intersection = intersections[ 0 ];\n\t\t\n\t\tif ( intersection.distance < 1 ) {\n\t\t\t\n\t\t\tdirection.reflect( intersection.face.normal );\n\t\t\t\n\t\t}\n\t\t\n\t}\n\n\tposition.add( velocity.copy( direction ).multiplyScalar( event.delta / 20 ) );\n\n}"
+				"source": "var ball = this.getObjectByName( 'Ball' );\n\nvar position = ball.position;\n\nvar velocity = new THREE.Vector3();\n\nvar direction = new THREE.Vector3();\ndirection.x = Math.random() - 0.5;\ndirection.z = Math.random() - 0.5;\ndirection.normalize().multiplyScalar( 0.1 );\n\nvar pad1 = this.getObjectByName( 'Pad 1' );\nvar pad2 = this.getObjectByName( 'Pad 2' );\n\nvar raycaster = new THREE.Raycaster();\nvar objects = [ pad1, pad2 ];\n\n//\n\nfunction mousemove( event ) {\n\n\tpad1.position.z = ( event.clientX / player.width ) * 3 - 1.5;\n\tpad2.position.z = - pad1.position.z;\n\n}\n\nfunction update( event ) {\n\t\n\tif ( position.x < -3 || position.x > 3 ) direction.x = - direction.x;\n\tif ( position.z < -2 || position.z > 2 ) direction.z = - direction.z;\n\t\n\tposition.x = Math.max( - 3, Math.min( 3, position.x ) );\n\tposition.z = Math.max( - 2, Math.min( 2, position.z ) );\n\t\n\traycaster.set( position, direction );\n\t\n\tvar intersections = raycaster.intersectObjects( objects );\n\t\n\tif ( intersections.length > 0 ) {\n\n\t\tvar intersection = intersections[ 0 ];\n\t\t\n\t\tif ( intersection.distance < 0.1 ) {\n\t\t\t\n\t\t\tdirection.reflect( intersection.face.normal );\n\t\t\t\n\t\t}\n\t\t\n\t}\n\n\tposition.add( velocity.copy( direction ).multiplyScalar( event.delta / 20 ) );\n\n}"
 			}]
 	}
 }

+ 8 - 7
editor/examples/shaders.app.json

@@ -10,28 +10,29 @@
 	},
 	"camera": {
 		"metadata": {
-			"version": 4.4,
+			"version": 4.5,
 			"type": "Object",
 			"generator": "Object3D.toJSON"
 		},
 		"object": {
-			"uuid": "FC3E973B-5A4A-4177-BD9C-A58E61E0593B",
+			"uuid": "4AC7ADED-CC22-4B16-8218-2E0A0C38C8F8",
 			"type": "PerspectiveCamera",
 			"name": "Camera",
+			"layers": 1,
 			"matrix": [0.605503,0,-0.795843,0,-0.261526,0.944464,-0.198978,0,0.751645,0.328615,0.571876,0,2.571484,1.124239,1.956469,1],
 			"fov": 50,
 			"zoom": 1,
 			"near": 0.1,
 			"far": 10000,
 			"focus": 10,
-			"aspect": 1.368715,
+			"aspect": 1.428977,
 			"filmGauge": 35,
 			"filmOffset": 0
 		}
 	},
 	"scene": {
 		"metadata": {
-			"version": 4.4,
+			"version": 4.5,
 			"type": "Object",
 			"generator": "Object3D.toJSON"
 		},
@@ -50,11 +51,9 @@
 				"depthTest": true,
 				"depthWrite": true,
 				"wireframe": true,
-				"skinning": false,
-				"morphTargets": false,
 				"uniforms": {
 					"time": {
-						"value": 0.0
+						"value": 0
 					}
 				},
 				"vertexShader": "uniform float time;\nvarying vec3 vPosition;\nvoid main() {\n\tvPosition = position;\n\tvPosition.x += sin( time + vPosition.z * 4.0 ) / 4.0;\n\tvPosition.y += cos( time + vPosition.z * 4.0 ) / 4.0;\n\tgl_Position = projectionMatrix * modelViewMatrix * vec4( vPosition, 1.0 );\n}",
@@ -64,12 +63,14 @@
 			"uuid": "5FC9ACA9-2A93-474D-AA32-FACC76551914",
 			"type": "Scene",
 			"name": "Scene",
+			"layers": 1,
 			"matrix": [1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1],
 			"children": [
 				{
 					"uuid": "FC7B6CF2-6386-4F47-9CE6-8ADB9FCA6E1F",
 					"type": "Mesh",
 					"name": "Icosahedron 1",
+					"layers": 1,
 					"matrix": [1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1],
 					"geometry": "EA781333-F3AE-470D-9110-A9724FCB42AA",
 					"material": "50ED51F1-DEA4-4B61-8082-BF41609E8C27"

+ 6 - 4
editor/index.html

@@ -60,8 +60,10 @@
 		<script src="../examples/js/loaders/ctm/lzma.js"></script>
 		<script src="../examples/js/loaders/ctm/ctm.js"></script>
 		<script src="../examples/js/loaders/ctm/CTMLoader.js"></script>
-		<script src="../examples/js/exporters/OBJExporter.js"></script>
+
+		<script src="../examples/js/exporters/ColladaExporter.js"></script>
 		<script src="../examples/js/exporters/GLTFExporter.js"></script>
+		<script src="../examples/js/exporters/OBJExporter.js"></script>
 		<script src="../examples/js/exporters/STLExporter.js"></script>
 
 		<script src="../examples/js/renderers/Projector.js"></script>
@@ -195,15 +197,15 @@
 			var viewport = new Viewport( editor );
 			document.body.appendChild( viewport.dom );
 
+			var toolbar = new Toolbar( editor );
+			document.body.appendChild( toolbar.dom );
+
 			var script = new Script( editor );
 			document.body.appendChild( script.dom );
 
 			var player = new Player( editor );
 			document.body.appendChild( player.dom );
 
-			var toolbar = new Toolbar( editor );
-			document.body.appendChild( toolbar.dom );
-
 			var menubar = new Menubar( editor );
 			document.body.appendChild( menubar.dom );
 

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

@@ -184,6 +184,24 @@ Menubar.File = function ( editor ) {
 
 	options.add( new UI.HorizontalRule() );
 
+	// Export DAE
+
+	var option = new UI.Row();
+	option.setClass( 'option' );
+	option.setTextContent( 'Export DAE' );
+	option.onClick( function () {
+
+		var exporter = new THREE.ColladaExporter();
+
+		exporter.parse( editor.scene, function ( result ) {
+
+			saveString( result.data, 'scene.dae' );
+
+		} );
+
+	} );
+	options.add( option );
+
 	// Export GLB
 
 	var option = new UI.Row();
@@ -200,7 +218,7 @@ Menubar.File = function ( editor ) {
 			// forceIndices: true, forcePowerOfTwoTextures: true
 			// to allow compatibility with facebook
 		}, { binary: true, forceIndices: true, forcePowerOfTwoTextures: true } );
-		
+
 	} );
 	options.add( option );
 

+ 9 - 3
editor/js/Toolbar.js

@@ -8,6 +8,7 @@ var Toolbar = function ( editor ) {
 
 	var container = new UI.Panel();
 	container.setId( 'toolbar' );
+	container.setDisplay( 'none' );
 
 	var buttons = new UI.Panel();
 	container.add( buttons );
@@ -15,7 +16,6 @@ var Toolbar = function ( editor ) {
 	// translate / rotate / scale
 
 	var translate = new UI.Button( 'translate' );
-	translate.dom.title = 'W';
 	translate.dom.className = 'Button selected';
 	translate.onClick( function () {
 
@@ -25,7 +25,6 @@ var Toolbar = function ( editor ) {
 	buttons.add( translate );
 
 	var rotate = new UI.Button( 'rotate' );
-	rotate.dom.title = 'E';
 	rotate.onClick( function () {
 
 		signals.transformModeChanged.dispatch( 'rotate' );
@@ -34,7 +33,6 @@ var Toolbar = function ( editor ) {
 	buttons.add( rotate );
 
 	var scale = new UI.Button( 'scale' );
-	scale.dom.title = 'R';
 	scale.onClick( function () {
 
 		signals.transformModeChanged.dispatch( 'scale' );
@@ -50,6 +48,14 @@ var Toolbar = function ( editor ) {
 	} );
 	buttons.add( local );
 
+	//
+
+	signals.objectSelected.add( function ( object ) {
+
+		container.setDisplay( object === null ? 'none' : '' );
+
+	} );
+
 	signals.transformModeChanged.add( function ( mode ) {
 
 		translate.dom.classList.remove( 'selected' );

+ 0 - 2
editor/js/Viewport.js

@@ -269,7 +269,6 @@ var Viewport = function ( editor ) {
 	var controls = new THREE.EditorControls( camera, container.dom );
 	controls.addEventListener( 'change', function () {
 
-		transformControls.update();
 		signals.cameraChanged.dispatch( camera );
 
 	} );
@@ -391,7 +390,6 @@ var Viewport = function ( editor ) {
 		if ( editor.selected === object ) {
 
 			selectionBox.setFromObject( object );
-			transformControls.update();
 
 		}
 

+ 1 - 1
examples/canvas_ascii_effect.html

@@ -72,7 +72,7 @@
 				light.position.set( - 500, - 500, - 500 );
 				scene.add( light );
 
-				sphere = new THREE.Mesh( new THREE.SphereGeometry( 200, 20, 10 ), new THREE.MeshLambertMaterial() );
+				sphere = new THREE.Mesh( new THREE.SphereBufferGeometry( 200, 20, 10 ), new THREE.MeshLambertMaterial() );
 				scene.add( sphere );
 
 				// Plane

+ 1 - 1
examples/canvas_camera_orthographic.html

@@ -58,7 +58,7 @@
 
 				// Cubes
 
-				var geometry = new THREE.BoxGeometry( 50, 50, 50 );
+				var geometry = new THREE.BoxBufferGeometry( 50, 50, 50 );
 				var material = new THREE.MeshLambertMaterial( { color: 0xffffff, overdraw: 0.5 } );
 
 				for ( var i = 0; i < 100; i ++ ) {

+ 1 - 1
examples/canvas_geometry_earth.html

@@ -72,7 +72,7 @@
 				var loader = new THREE.TextureLoader();
 				loader.load( 'textures/land_ocean_ice_cloud_2048.jpg', function ( texture ) {
 
-					var geometry = new THREE.SphereGeometry( 200, 20, 20 );
+					var geometry = new THREE.SphereBufferGeometry( 200, 20, 20 );
 
 					var material = new THREE.MeshBasicMaterial( { map: texture, overdraw: 0.5 } );
 					var mesh = new THREE.Mesh( geometry, material );

+ 1 - 1
examples/canvas_geometry_hierarchy.html

@@ -52,7 +52,7 @@
 				scene = new THREE.Scene();
 				scene.background = new THREE.Color( 0xffffff );
 
-				var geometry = new THREE.BoxGeometry( 100, 100, 100 );
+				var geometry = new THREE.BoxBufferGeometry( 100, 100, 100 );
 				var material = new THREE.MeshNormalMaterial( { overdraw: 0.5 } );
 
 				group = new THREE.Group();

+ 1 - 1
examples/canvas_geometry_panorama.html

@@ -81,7 +81,7 @@
 
 				];
 
-				var geometry = new THREE.BoxGeometry( 300, 300, 300, 7, 7, 7 );
+				var geometry = new THREE.BoxBufferGeometry( 300, 300, 300, 7, 7, 7 );
 				geometry.scale( - 1, 1, 1 );
 
 				mesh = new THREE.Mesh( geometry, materials );

+ 7 - 5
examples/canvas_geometry_panorama_fisheye.html

@@ -81,18 +81,20 @@
 
 				];
 
-				var geometry = new THREE.BoxGeometry( 300, 300, 300, 7, 7, 7 );
+				var geometry = new THREE.BoxBufferGeometry( 300, 300, 300, 7, 7, 7 );
 				geometry.scale( - 1, 1, 1 );
 
 				mesh = new THREE.Mesh( geometry, materials );
 				scene.add( mesh );
 
-				for ( var i = 0, l = mesh.geometry.vertices.length; i < l; i ++ ) {
+				var vec = new THREE.Vector3();
+				var positions = mesh.geometry.attributes.position;
 
-					var vertex = mesh.geometry.vertices[ i ];
+				for ( var i = 0, l = positions.count; i < l; i ++ ) {
 
-					vertex.normalize();
-					vertex.multiplyScalar( 550 );
+					vec.fromBufferAttribute( positions, i );
+					vec.setLength( 550 );
+					positions.setXYZ( i, vec.x, vec.y, vec.z );
 
 				}
 

+ 1 - 1
examples/canvas_geometry_shapes.html

@@ -71,7 +71,7 @@
 
 					// flat shape
 
-					var geometry = new THREE.ShapeGeometry( shape );
+					var geometry = new THREE.ShapeBufferGeometry( shape );
 					var material = new THREE.MeshBasicMaterial( { color: color, overdraw: 0.5 } );
 
 					var mesh = new THREE.Mesh( geometry, material );

+ 11 - 10
examples/canvas_geometry_terrain.html

@@ -71,17 +71,19 @@
 
 				var data = generateHeight( 1024, 1024 );
 				var texture = new THREE.CanvasTexture( generateTexture( data, 1024, 1024 ) );
-				var material = new THREE.MeshBasicMaterial( { map: texture, overdraw: 0.5 } );
+				var material = new THREE.MeshBasicMaterial( { map: texture, side: THREE.DoubleSide, overdraw: 0.5 } );
 
 				var quality = 16, step = 1024 / quality;
 
-				var geometry = new THREE.PlaneGeometry( 2000, 2000, quality - 1, quality - 1 );
+				var geometry = new THREE.PlaneBufferGeometry( 2000, 2000, quality - 1, quality - 1 );
 				geometry.rotateX( - Math.PI / 2 );
 
-				for ( var i = 0, l = geometry.vertices.length; i < l; i ++ ) {
+				var positions = geometry.attributes.position;
+
+				for ( var i = 0, l = positions.count; i < l; i ++ ) {
 
 					var x = i % quality, y = Math.floor( i / quality );
-					geometry.vertices[ i ].y = data[ ( x * step ) + ( y * step ) * 1024 ] * 2 - 128;
+					positions.setY( i, data[ ( x * step ) + ( y * step ) * 1024 ] * 2 - 128 );
 
 				}
 
@@ -122,7 +124,7 @@
 			function generateHeight( width, height ) {
 
 				var data = new Uint8Array( width * height ), perlin = new ImprovedNoise(),
-				size = width * height, quality = 2, z = Math.random() * 100;
+					size = width * height, quality = 2, z = Math.random() * 100;
 
 				for ( var j = 0; j < 4; j ++ ) {
 
@@ -130,7 +132,7 @@
 
 					for ( var i = 0; i < size; i ++ ) {
 
-						var x = i % width, y = ~~ ( i / width );
+						var x = i % width, y = ~ ~ ( i / width );
 						data[ i ] += Math.abs( perlin.noise( x / quality, y / quality, z ) * 0.5 ) * quality + 10;
 
 					}
@@ -144,7 +146,7 @@
 			function generateTexture( data, width, height ) {
 
 				var canvas, context, image, imageData,
-				level, diff, vector3, sun, shade;
+					level, diff, vector3, sun, shade;
 
 				vector3 = new THREE.Vector3( 0, 0, 0 );
 
@@ -162,7 +164,7 @@
 				image = context.getImageData( 0, 0, width, height );
 				imageData = image.data;
 
-				for ( var i = 0, j = 0, l = imageData.length; i < l; i += 4, j ++  ) {
+				for ( var i = 0, j = 0, l = imageData.length; i < l; i += 4, j ++ ) {
 
 					vector3.x = data[ j - 1 ] - data[ j + 1 ];
 					vector3.y = 2;
@@ -183,7 +185,7 @@
 
 			}
 
-			function onDocumentMouseMove(event) {
+			function onDocumentMouseMove( event ) {
 
 				mouseX = event.clientX - windowHalfX;
 				mouseY = event.clientY - windowHalfY;
@@ -211,7 +213,6 @@
 
 			}
 
-
 		</script>
 
 	</body>

+ 1 - 1
examples/canvas_geometry_text.html

@@ -80,7 +80,7 @@
 
 				}
 
-				var geometry = new THREE.TextGeometry( theText, {
+				var geometry = new THREE.TextBufferGeometry( theText, {
 
 					font: font,
 					size: 80,

+ 1 - 1
examples/canvas_interactive_cubes.html

@@ -55,7 +55,7 @@
 				scene = new THREE.Scene();
 				scene.background = new THREE.Color( 0xf0f0f0 );
 
-				var geometry = new THREE.BoxGeometry( 100, 100, 100 );
+				var geometry = new THREE.BoxBufferGeometry( 100, 100, 100 );
 
 				for ( var i = 0; i < 10; i ++ ) {
 

+ 1 - 1
examples/canvas_interactive_cubes_tween.html

@@ -54,7 +54,7 @@
 				scene = new THREE.Scene();
 				scene.background = new THREE.Color( 0xf0f0f0 );
 
-				var geometry = new THREE.BoxGeometry( 100, 100, 100 );
+				var geometry = new THREE.BoxBufferGeometry( 100, 100, 100 );
 
 				for ( var i = 0; i < 20; i ++ ) {
 

+ 1 - 1
examples/canvas_interactive_voxelpainter.html

@@ -28,7 +28,7 @@
 
 			var mouse, raycaster, isShiftDown = false;
 
-			var cubeGeometry = new THREE.BoxGeometry( 50, 50, 50 );
+			var cubeGeometry = new THREE.BoxBufferGeometry( 50, 50, 50 );
 			var cubeMaterial = new THREE.MeshLambertMaterial( { color: 0x00ff80, overdraw: 0.5 } );
 
 			var objects = [];

+ 42 - 21
examples/canvas_lines_colors.html

@@ -49,14 +49,11 @@
 		<script src="js/renderers/Projector.js"></script>
 		<script src="js/renderers/CanvasRenderer.js"></script>
 
-		<script src="js/Detector.js"></script>
 		<script src="js/geometries/hilbert3D.js"></script>
 		<script src="js/geometries/hilbert2D.js"></script>
 
 		<script>
 
-			if ( ! Detector.webgl ) Detector.addGetWebGLMessage();
-
 			var mouseX = 0, mouseY = 0;
 
 			var windowHalfX = window.innerWidth / 2;
@@ -81,37 +78,61 @@
 
 				// 2d lines
 
-				var geometry2 = new THREE.Geometry(),
-					points = hilbert2D( new THREE.Vector3( 0, 0, 0 ), 400, 4 ),
-					colors2 = [];
+				var vertices = hilbert2D( new THREE.Vector3( 0, 0, 0 ), 400, 4 );
+				var numVertices = vertices.length;
 
-				for ( var i = 0; i < points.length; i ++ ) {
+				var geometry2 = new THREE.BufferGeometry();
 
-					geometry2.vertices.push( points[ i ] );
+				var positions = new Float32Array( numVertices * 3 );
+				var colors = new Float32Array( numVertices * 3 );
 
-					colors2[ i ] = new THREE.Color( 0xffffff );
-					colors2[ i ].setHSL( i / points.length, 1, 0.5 );
+				geometry2.addAttribute( 'position', new THREE.BufferAttribute( positions, 3 ) );
+				geometry2.addAttribute( 'color', new THREE.BufferAttribute( colors, 3 ) );
 
-				}
+				var color = new THREE.Color();
+
+				for ( var i = 0, index = 0, l = numVertices; i < l; i ++, index += 3 ) {
+
+					positions[ index ] = vertices[ i ].x;
+					positions[ index + 1 ] = vertices[ i ].y;
+					positions[ index + 2 ] = vertices[ i ].z;
 
-				geometry2.colors = colors2;
+					color.setHSL( i / l, 1.0, 0.5 );
+
+					colors[ index ] = color.r;
+					colors[ index + 1 ] = color.g;
+					colors[ index + 2 ] = color.b;
+
+				}
 
 				// 3d lines
 
-				var geometry3 = new THREE.Geometry(),
-					points = hilbert3D( new THREE.Vector3( 0, 0, 0 ), 200, 2, 0, 1, 2, 3, 4, 5, 6, 7 ),
-					colors3 = [];
+				var vertices = hilbert3D( new THREE.Vector3( 0, 0, 0 ), 200, 2, 0, 1, 2, 3, 4, 5, 6, 7 );
+				var numVertices = vertices.length;
 
-				for ( var i = 0; i < points.length; i ++ ) {
+				var geometry3 = new THREE.BufferGeometry();
 
-					geometry3.vertices.push( points[ i ] );
+				var positions = new Float32Array( numVertices * 3 );
+				var colors = new Float32Array( numVertices * 3 );
 
-					colors3[ i ] = new THREE.Color( 0xffffff );
-					colors3[ i ].setHSL( i / points.length, 1, 0.5 );
+				geometry3.addAttribute( 'position', new THREE.BufferAttribute( positions, 3 ) );
+				geometry3.addAttribute( 'color', new THREE.BufferAttribute( colors, 3 ) );
 
-				}
+				var color = new THREE.Color();
+
+				for ( var i = 0, index = 0, l = numVertices; i < l; i ++, index += 3 ) {
+
+					positions[ index ] = vertices[ i ].x;
+					positions[ index + 1 ] = vertices[ i ].y;
+					positions[ index + 2 ] = vertices[ i ].z;
 
-				geometry3.colors = colors3;
+					color.setHSL( i / l, 1.0, 0.5 );
+
+					colors[ index ] = color.r;
+					colors[ index + 1 ] = color.g;
+					colors[ index + 2 ] = color.b;
+
+				}
 
 				// lines
 

+ 25 - 58
examples/canvas_lines_dashed.html

@@ -38,17 +38,13 @@
 
 		<script src="js/geometries/hilbert3D.js"></script>
 
-		<script src="js/Detector.js"></script>
 		<script src="js/libs/stats.min.js"></script>
 
 		<script>
 
-			if ( ! Detector.webgl ) Detector.addGetWebGLMessage();
-
 			var renderer, scene, camera, stats;
 			var objects = [];
 
-
 			var WIDTH = window.innerWidth, HEIGHT = window.innerHeight;
 
 			init();
@@ -62,23 +58,43 @@
 				scene = new THREE.Scene();
 				scene.background = new THREE.Color( 0x111111 );
 
-				var subdivisions = 6;
+				var subdivisions = 3;
 				var recursion = 1;
 
 				var points = hilbert3D( new THREE.Vector3( 0,0,0 ), 25.0, recursion, 0, 1, 2, 3, 4, 5, 6, 7 );
 				var spline = new THREE.CatmullRomCurve3( points );
 
 				var samples = spline.getPoints( points.length * subdivisions );
-				var geometrySpline = new THREE.Geometry().setFromPoints( samples );
+				var numSamples = samples.length;
+
+				var geometrySpline = new THREE.BufferGeometry();
+
+				var positions = new Float32Array( numSamples * 3 );
+
+				geometrySpline.addAttribute( 'position', new THREE.BufferAttribute( positions, 3 ) );
+
+				for ( var i = 0, index = 0, l = numSamples; i < l; i ++, index += 3 ) {
+
+					positions[ index ] = samples[ i ].x;
+					positions[ index + 1 ] = samples[ i ].y;
+					positions[ index + 2 ] = samples[ i ].z;
 
-				var geometryCube = cube( 50 );
+				}
+
+				var geometryCube = new THREE.EdgesGeometry( new THREE.BoxBufferGeometry( 50, 50, 50 ) );
 
-				var object = new THREE.Line( geometrySpline, new THREE.LineDashedMaterial( { color: 0xffffff, dashSize: 1, gapSize: 0.5 } ) );
+				var object = new THREE.Line(
+					geometrySpline,
+					new THREE.LineDashedMaterial( { color: 0xffffff, dashSize: 10, gapSize: 10, linewidth: 3 } )
+				);
 
 				objects.push( object );
 				scene.add( object );
 
-				var object = new THREE.LineSegments( geometryCube, new THREE.LineDashedMaterial( { color: 0xffaa00, dashSize: 30, gapSize: 10, linewidth: 2 } ) );
+				var object = new THREE.LineSegments(
+					geometryCube,
+					new THREE.LineDashedMaterial( { color: 0xffaa00, dashSize: 30, gapSize: 10, linewidth: 3 } )
+				);
 
 				objects.push( object );
 				scene.add( object );
@@ -99,55 +115,6 @@
 
 			}
 
-			function cube( size ) {
-
-				var h = size * 0.5;
-
-				var geometry = new THREE.Geometry();
-
-				geometry.vertices.push(
-					new THREE.Vector3( -h, -h, -h ),
-					new THREE.Vector3( -h, h, -h ),
-
-					new THREE.Vector3( -h, h, -h ),
-					new THREE.Vector3( h, h, -h ),
-
-					new THREE.Vector3( h, h, -h ),
-					new THREE.Vector3( h, -h, -h ),
-
-					new THREE.Vector3( h, -h, -h ),
-					new THREE.Vector3( -h, -h, -h ),
-
-
-					new THREE.Vector3( -h, -h, h ),
-					new THREE.Vector3( -h, h, h ),
-
-					new THREE.Vector3( -h, h, h ),
-					new THREE.Vector3( h, h, h ),
-
-					new THREE.Vector3( h, h, h ),
-					new THREE.Vector3( h, -h, h ),
-
-					new THREE.Vector3( h, -h, h ),
-					new THREE.Vector3( -h, -h, h ),
-
-					new THREE.Vector3( -h, -h, -h ),
-					new THREE.Vector3( -h, -h, h ),
-
-					new THREE.Vector3( -h, h, -h ),
-					new THREE.Vector3( -h, h, h ),
-
-					new THREE.Vector3( h, h, -h ),
-					new THREE.Vector3( h, h, h ),
-
-					new THREE.Vector3( h, -h, -h ),
-					new THREE.Vector3( h, -h, h )
-				 );
-
-				return geometry;
-
-			}
-
 			function onWindowResize() {
 
 				camera.aspect = window.innerWidth / window.innerHeight;

+ 23 - 20
examples/canvas_lines_sphere.html

@@ -26,29 +26,28 @@
 		<script>
 
 			var SCREEN_WIDTH = window.innerWidth,
-			SCREEN_HEIGHT = window.innerHeight,
+				SCREEN_HEIGHT = window.innerHeight,
 
-			mouseX = 0, mouseY = 0,
+				mouseX = 0, mouseY = 0,
 
-			windowHalfX = window.innerWidth / 2,
-			windowHalfY = window.innerHeight / 2,
+				windowHalfX = window.innerWidth / 2,
+				windowHalfY = window.innerHeight / 2,
 
-			SEPARATION = 200,
-			AMOUNTX = 10,
-			AMOUNTY = 10,
+				SEPARATION = 200,
+				AMOUNTX = 10,
+				AMOUNTY = 10,
 
-			camera, scene, renderer;
+				camera, scene, renderer;
 
 			init();
 			animate();
 
 			function init() {
 
-				var container, separation = 100, amountX = 50, amountY = 50,
-				particles, particle;
+				var container, separation = 100, amountX = 50, amountY = 50, particles, particle;
 
-				container = document.createElement('div');
-				document.body.appendChild(container);
+				container = document.createElement( 'div' );
+				document.body.appendChild( container );
 
 				camera = new THREE.PerspectiveCamera( 75, SCREEN_WIDTH / SCREEN_HEIGHT, 1, 10000 );
 				camera.position.z = 1000;
@@ -83,7 +82,7 @@
 					particle.position.y = Math.random() * 2 - 1;
 					particle.position.z = Math.random() * 2 - 1;
 					particle.position.normalize();
-					particle.position.multiplyScalar( Math.random() * 10 + 450 );
+					particle.position.multiplyScalar( 450 );
 					particle.scale.multiplyScalar( 2 );
 					scene.add( particle );
 
@@ -91,25 +90,28 @@
 
 				// lines
 
-				for (var i = 0; i < 300; i++) {
+				var vertices = [];
 
-					var geometry = new THREE.Geometry();
+				for ( var i = 0; i < 300; i ++ ) {
 
 					var vertex = new THREE.Vector3( Math.random() * 2 - 1, Math.random() * 2 - 1, Math.random() * 2 - 1 );
 					vertex.normalize();
 					vertex.multiplyScalar( 450 );
 
-					geometry.vertices.push( vertex );
+					vertices.push( vertex );
 
 					var vertex2 = vertex.clone();
 					vertex2.multiplyScalar( Math.random() * 0.3 + 1 );
 
-					geometry.vertices.push( vertex2 );
+					vertices.push( vertex2 );
 
-					var line = new THREE.Line( geometry, new THREE.LineBasicMaterial( { color: 0xffffff, opacity: Math.random() } ) );
-					scene.add( line );
 				}
 
+				var geometry = new THREE.BufferGeometry().setFromPoints( vertices );
+
+				var line = new THREE.LineSegments( geometry, new THREE.LineBasicMaterial( { color: 0xffffff } ) );
+				scene.add( line );
+
 				document.addEventListener( 'mousemove', onDocumentMouseMove, false );
 				document.addEventListener( 'touchstart', onDocumentTouchStart, false );
 				document.addEventListener( 'touchmove', onDocumentTouchMove, false );
@@ -134,10 +136,11 @@
 
 			//
 
-			function onDocumentMouseMove(event) {
+			function onDocumentMouseMove( event ) {
 
 				mouseX = event.clientX - windowHalfX;
 				mouseY = event.clientY - windowHalfY;
+
 			}
 
 			function onDocumentTouchStart( event ) {

+ 1 - 1
examples/canvas_materials_video.html

@@ -103,7 +103,7 @@
 
 				//
 
-				var plane = new THREE.PlaneGeometry( 480, 204, 4, 4 );
+				var plane = new THREE.PlaneBufferGeometry( 480, 204, 4, 4 );
 
 				mesh = new THREE.Mesh( plane, material );
 				mesh.scale.x = mesh.scale.y = mesh.scale.z = 1.5;

+ 1 - 1
examples/canvas_performance.html

@@ -53,7 +53,7 @@
 
 				// Spheres
 
-				var geometry = new THREE.SphereGeometry( 100, 26, 18 );
+				var geometry = new THREE.SphereBufferGeometry( 100, 26, 18 );
 				var material = new THREE.MeshLambertMaterial( { color: 0xffffff, overdraw: 0.5 } );
 
 				for ( var i = 0; i < 20; i ++ ) {

+ 1 - 1
examples/canvas_sandbox.html

@@ -56,7 +56,7 @@
 				group = new THREE.Group();
 				scene.add( group );
 
-				var geometry = new THREE.IcosahedronGeometry( 100, 1 );
+				var geometry = new THREE.IcosahedronBufferGeometry( 100, 1 );
 
 				var envMap = new THREE.TextureLoader().load( 'textures/metal.jpg' );
 				envMap.mapping = THREE.SphericalReflectionMapping;

+ 7 - 1
examples/files.js

@@ -183,6 +183,7 @@ var files = {
 		"webgl_materials_video",
 		"webgl_materials_video_webcam",
 		"webgl_materials_wireframe",
+		"webgl_math_orientation_transform",
 		"webgl_mirror",
 		"webgl_mirror_nodes",
 		"webgl_modifier_simplifier",
@@ -209,6 +210,7 @@ var files = {
 		"webgl_panorama_equirectangular",
 		"webgl_performance",
 		"webgl_performance_doublesided",
+		"webgl_performance_nodes",
 		"webgl_performance_static",
 		"webgl_physics_cloth",
 		"webgl_physics_convex_break",
@@ -220,6 +222,7 @@ var files = {
 		"webgl_points_sprites",
 		"webgl_postprocessing",
 		"webgl_postprocessing_advanced",
+		"webgl_postprocessing_afterimage",
 		"webgl_postprocessing_backgrounds",
 		"webgl_postprocessing_crossfade",
 		"webgl_postprocessing_dof",
@@ -232,6 +235,7 @@ var files = {
 		"webgl_postprocessing_ssaa",
 		"webgl_postprocessing_ssaa_unbiased",
 		"webgl_postprocessing_nodes",
+		"webgl_postprocessing_nodes_pass",
 		"webgl_postprocessing_outline",
 		"webgl_postprocessing_pixel",
 		"webgl_postprocessing_procedural",
@@ -241,6 +245,7 @@ var files = {
 		"webgl_postprocessing_ssao",
 		"webgl_postprocessing_taa",
 		"webgl_postprocessing_unreal_bloom",
+		"webgl_raycast_sprite",
 		"webgl_raycast_texture",
 		"webgl_read_float_buffer",
 		"webgl_refraction",
@@ -323,10 +328,11 @@ var files = {
 		"webvr_dragging",
 		"webvr_lorenzattractor",
 		"webvr_panorama",
+		"webvr_paint",
 		"webvr_rollercoaster",
 		"webvr_sandbox",
+		"webvr_sculpt",
 		"webvr_video",
-		"webvr_vive",
 		"webvr_vive_paint",
 		"webvr_vive_sculpt"
 	],

+ 2 - 4
examples/index.html

@@ -309,11 +309,9 @@
 			link.setAttribute( 'target', 'viewer' );
 			link.addEventListener( 'click', function ( event ) {
 
-				if ( event.button === 0 ) {
+				if ( event.button !== 0 || event.ctrlKey || event.altKey || event.metaKey ) return;
 
-					selectFile( file );
-
-				}
+				selectFile( file );
 
 			} );
 

+ 13 - 0
examples/js/Detector.js

@@ -18,6 +18,19 @@ var Detector = {
 
 		}
 
+	} )(),
+	webgl2: ( function () {
+
+		try {
+
+			var canvas = document.createElement( 'canvas' ); return !! ( window.WebGL2RenderingContext && ( canvas.getContext( 'webgl2' ) ) );
+
+		} catch ( e ) {
+
+			return false;
+
+		}
+
 	} )(),
 	workers: !! window.Worker,
 	fileapi: window.File && window.FileReader && window.FileList && window.Blob,

+ 769 - 752
examples/js/MarchingCubes.js

@@ -4,548 +4,573 @@
  * Port of http://webglsamples.org/blob/blob.html
  */
 
-THREE.MarchingCubes = function ( resolution, material, enableUvs, enableColors ) {
+( function ( THREE ) {
 
-	THREE.ImmediateRenderObject.call( this, material );
+	THREE.MarchingCubes = function ( resolution, material, enableUvs, enableColors ) {
 
-	var scope = this;
+		THREE.ImmediateRenderObject.call( this, material );
 
-	// temp buffers used in polygonize
+		var scope = this;
 
-	var vlist = new Float32Array( 12 * 3 );
-	var nlist = new Float32Array( 12 * 3 );
+		var yd, zd;
+		var field, normal_cache;
 
-	this.enableUvs = enableUvs !== undefined ? enableUvs : false;
-	this.enableColors = enableColors !== undefined ? enableColors : false;
+		// temp buffers used in polygonize
 
-	// functions have to be object properties
-	// prototype functions kill performance
-	// (tested and it was 4x slower !!!)
+		var vlist = new Float32Array( 12 * 3 );
+		var nlist = new Float32Array( 12 * 3 );
 
-	this.init = function ( resolution ) {
+		this.enableUvs = enableUvs !== undefined ? enableUvs : false;
+		this.enableColors = enableColors !== undefined ? enableColors : false;
 
-		this.resolution = resolution;
+		// functions have to be object properties
+		// prototype functions kill performance
+		// (tested and it was 4x slower !!!)
 
-		// parameters
+		this.init = function ( resolution ) {
 
-		this.isolation = 80.0;
+			this.resolution = resolution;
 
-		// size of field, 32 is pushing it in Javascript :)
+			// parameters
 
-		this.size = resolution;
-		this.size2 = this.size * this.size;
-		this.size3 = this.size2 * this.size;
-		this.halfsize = this.size / 2.0;
+			this.isolation = 80.0;
 
-		// deltas
+			// size of field, 32 is pushing it in Javascript :)
 
-		this.delta = 2.0 / this.size;
-		this.yd = this.size;
-		this.zd = this.size2;
+			this.size = resolution;
+			this.size2 = this.size * this.size;
+			this.size3 = this.size2 * this.size;
+			this.halfsize = this.size / 2.0;
 
-		this.field = new Float32Array( this.size3 );
-		this.normal_cache = new Float32Array( this.size3 * 3 );
+			// deltas
 
-		// immediate render mode simulator
+			this.delta = 2.0 / this.size;
 
-		this.maxCount = 4096; // TODO: find the fastest size for this buffer
-		this.count = 0;
+			yd = this.size;
+			zd = this.size2;
 
-		this.hasPositions = false;
-		this.hasNormals = false;
-		this.hasColors = false;
-		this.hasUvs = false;
+			field = new Float32Array( this.size3 );
+			normal_cache = new Float32Array( this.size3 * 3 );
 
-		this.positionArray = new Float32Array( this.maxCount * 3 );
-		this.normalArray = new Float32Array( this.maxCount * 3 );
+			// immediate render mode simulator
 
-		if ( this.enableUvs ) {
+			this.maxCount = 4096; // TODO: find the fastest size for this buffer
+			this.count = 0;
 
-			this.uvArray = new Float32Array( this.maxCount * 2 );
+			this.hasPositions = false;
+			this.hasNormals = false;
+			this.hasColors = false;
+			this.hasUvs = false;
 
-		}
+			this.positionArray = new Float32Array( this.maxCount * 3 );
+			this.normalArray = new Float32Array( this.maxCount * 3 );
 
-		if ( this.enableColors ) {
+			if ( this.enableUvs ) {
 
-			this.colorArray = new Float32Array( this.maxCount * 3 );
+				this.uvArray = new Float32Array( this.maxCount * 2 );
 
-		}
+			}
 
-	};
+			if ( this.enableColors ) {
 
-	///////////////////////
-	// Polygonization
-	///////////////////////
+				this.colorArray = new Float32Array( this.maxCount * 3 );
 
-	function lerp( a, b, t ) {
+			}
 
-		return a + ( b - a ) * t;
+		};
 
-	}
+		///////////////////////
+		// Polygonization
+		///////////////////////
 
-	function VIntX( q, offset, isol, x, y, z, valp1, valp2 ) {
+		function lerp( a, b, t ) {
 
-		var mu = ( isol - valp1 ) / ( valp2 - valp1 ),
-			nc = scope.normal_cache;
+			return a + ( b - a ) * t;
 
-		vlist[ offset + 0 ] = x + mu * scope.delta;
-		vlist[ offset + 1 ] = y;
-		vlist[ offset + 2 ] = z;
+		}
 
-		nlist[ offset + 0 ] = lerp( nc[ q + 0 ], nc[ q + 3 ], mu );
-		nlist[ offset + 1 ] = lerp( nc[ q + 1 ], nc[ q + 4 ], mu );
-		nlist[ offset + 2 ] = lerp( nc[ q + 2 ], nc[ q + 5 ], mu );
+		function VIntX( q, offset, isol, x, y, z, valp1, valp2 ) {
 
-	}
+			var mu = ( isol - valp1 ) / ( valp2 - valp1 );
 
-	function VIntY( q, offset, isol, x, y, z, valp1, valp2 ) {
+			vlist[ offset + 0 ] = x + mu * scope.delta;
+			vlist[ offset + 1 ] = y;
+			vlist[ offset + 2 ] = z;
 
-		var mu = ( isol - valp1 ) / ( valp2 - valp1 ),
-			nc = scope.normal_cache;
+			nlist[ offset + 0 ] = lerp( normal_cache[ q + 0 ], normal_cache[ q + 3 ], mu );
+			nlist[ offset + 1 ] = lerp( normal_cache[ q + 1 ], normal_cache[ q + 4 ], mu );
+			nlist[ offset + 2 ] = lerp( normal_cache[ q + 2 ], normal_cache[ q + 5 ], mu );
 
-		vlist[ offset + 0 ] = x;
-		vlist[ offset + 1 ] = y + mu * scope.delta;
-		vlist[ offset + 2 ] = z;
+		}
 
-		var q2 = q + scope.yd * 3;
+		function VIntY( q, offset, isol, x, y, z, valp1, valp2 ) {
 
-		nlist[ offset + 0 ] = lerp( nc[ q + 0 ], nc[ q2 + 0 ], mu );
-		nlist[ offset + 1 ] = lerp( nc[ q + 1 ], nc[ q2 + 1 ], mu );
-		nlist[ offset + 2 ] = lerp( nc[ q + 2 ], nc[ q2 + 2 ], mu );
+			var mu = ( isol - valp1 ) / ( valp2 - valp1 );
 
-	}
+			vlist[ offset + 0 ] = x;
+			vlist[ offset + 1 ] = y + mu * scope.delta;
+			vlist[ offset + 2 ] = z;
 
-	function VIntZ( q, offset, isol, x, y, z, valp1, valp2 ) {
+			var q2 = q + yd * 3;
 
-		var mu = ( isol - valp1 ) / ( valp2 - valp1 ),
-			nc = scope.normal_cache;
+			nlist[ offset + 0 ] = lerp( normal_cache[ q + 0 ], normal_cache[ q2 + 0 ], mu );
+			nlist[ offset + 1 ] = lerp( normal_cache[ q + 1 ], normal_cache[ q2 + 1 ], mu );
+			nlist[ offset + 2 ] = lerp( normal_cache[ q + 2 ], normal_cache[ q2 + 2 ], mu );
 
-		vlist[ offset + 0 ] = x;
-		vlist[ offset + 1 ] = y;
-		vlist[ offset + 2 ] = z + mu * scope.delta;
+		}
 
-		var q2 = q + scope.zd * 3;
+		function VIntZ( q, offset, isol, x, y, z, valp1, valp2 ) {
 
-		nlist[ offset + 0 ] = lerp( nc[ q + 0 ], nc[ q2 + 0 ], mu );
-		nlist[ offset + 1 ] = lerp( nc[ q + 1 ], nc[ q2 + 1 ], mu );
-		nlist[ offset + 2 ] = lerp( nc[ q + 2 ], nc[ q2 + 2 ], mu );
+			var mu = ( isol - valp1 ) / ( valp2 - valp1 );
 
-	}
+			vlist[ offset + 0 ] = x;
+			vlist[ offset + 1 ] = y;
+			vlist[ offset + 2 ] = z + mu * scope.delta;
 
-	function compNorm( q ) {
+			var q2 = q + zd * 3;
 
-		var q3 = q * 3;
+			nlist[ offset + 0 ] = lerp( normal_cache[ q + 0 ], normal_cache[ q2 + 0 ], mu );
+			nlist[ offset + 1 ] = lerp( normal_cache[ q + 1 ], normal_cache[ q2 + 1 ], mu );
+			nlist[ offset + 2 ] = lerp( normal_cache[ q + 2 ], normal_cache[ q2 + 2 ], mu );
 
-		if ( scope.normal_cache[ q3 ] === 0.0 ) {
+		}
 
-			scope.normal_cache[ q3 + 0 ] = scope.field[ q - 1 ] - scope.field[ q + 1 ];
-			scope.normal_cache[ q3 + 1 ] = scope.field[ q - scope.yd ] - scope.field[ q + scope.yd ];
-			scope.normal_cache[ q3 + 2 ] = scope.field[ q - scope.zd ] - scope.field[ q + scope.zd ];
+		function compNorm( q ) {
 
-		}
+			var q3 = q * 3;
 
-	}
+			if ( normal_cache[ q3 ] === 0.0 ) {
 
-	// Returns total number of triangles. Fills triangles.
-	// (this is where most of time is spent - it's inner work of O(n3) loop )
+				normal_cache[ q3 + 0 ] = field[ q - 1 ] - field[ q + 1 ];
+				normal_cache[ q3 + 1 ] = field[ q - yd ] - field[ q + yd ];
+				normal_cache[ q3 + 2 ] = field[ q - zd ] - field[ q + zd ];
 
-	function polygonize( fx, fy, fz, q, isol, renderCallback ) {
+			}
 
-		// cache indices
-		var q1 = q + 1,
-			qy = q + scope.yd,
-			qz = q + scope.zd,
-			q1y = q1 + scope.yd,
-			q1z = q1 + scope.zd,
-			qyz = q + scope.yd + scope.zd,
-			q1yz = q1 + scope.yd + scope.zd;
+		}
 
-		var cubeindex = 0,
-			field0 = scope.field[ q ],
-			field1 = scope.field[ q1 ],
-			field2 = scope.field[ qy ],
-			field3 = scope.field[ q1y ],
-			field4 = scope.field[ qz ],
-			field5 = scope.field[ q1z ],
-			field6 = scope.field[ qyz ],
-			field7 = scope.field[ q1yz ];
+		// Returns total number of triangles. Fills triangles.
+		// (this is where most of time is spent - it's inner work of O(n3) loop )
+
+		function polygonize( fx, fy, fz, q, isol, renderCallback ) {
+
+			// cache indices
+			var q1 = q + 1,
+				qy = q + yd,
+				qz = q + zd,
+				q1y = q1 + yd,
+				q1z = q1 + zd,
+				qyz = q + yd + zd,
+				q1yz = q1 + yd + zd;
+
+			var cubeindex = 0,
+				field0 = field[ q ],
+				field1 = field[ q1 ],
+				field2 = field[ qy ],
+				field3 = field[ q1y ],
+				field4 = field[ qz ],
+				field5 = field[ q1z ],
+				field6 = field[ qyz ],
+				field7 = field[ q1yz ];
+
+			if ( field0 < isol ) cubeindex |= 1;
+			if ( field1 < isol ) cubeindex |= 2;
+			if ( field2 < isol ) cubeindex |= 8;
+			if ( field3 < isol ) cubeindex |= 4;
+			if ( field4 < isol ) cubeindex |= 16;
+			if ( field5 < isol ) cubeindex |= 32;
+			if ( field6 < isol ) cubeindex |= 128;
+			if ( field7 < isol ) cubeindex |= 64;
+
+			// if cube is entirely in/out of the surface - bail, nothing to draw
+
+			var bits = EdgeTable[ cubeindex ];
+			if ( bits === 0 ) return 0;
+
+			var d = scope.delta,
+				fx2 = fx + d,
+				fy2 = fy + d,
+				fz2 = fz + d;
+
+			// top of the cube
+
+			if ( bits & 1 ) {
+
+				compNorm( q );
+				compNorm( q1 );
+				VIntX( q * 3, 0, isol, fx, fy, fz, field0, field1 );
 
-		if ( field0 < isol ) cubeindex |= 1;
-		if ( field1 < isol ) cubeindex |= 2;
-		if ( field2 < isol ) cubeindex |= 8;
-		if ( field3 < isol ) cubeindex |= 4;
-		if ( field4 < isol ) cubeindex |= 16;
-		if ( field5 < isol ) cubeindex |= 32;
-		if ( field6 < isol ) cubeindex |= 128;
-		if ( field7 < isol ) cubeindex |= 64;
+			}
 
-		// if cube is entirely in/out of the surface - bail, nothing to draw
+			if ( bits & 2 ) {
 
-		var bits = THREE.edgeTable[ cubeindex ];
-		if ( bits === 0 ) return 0;
+				compNorm( q1 );
+				compNorm( q1y );
+				VIntY( q1 * 3, 3, isol, fx2, fy, fz, field1, field3 );
 
-		var d = scope.delta,
-			fx2 = fx + d,
-			fy2 = fy + d,
-			fz2 = fz + d;
+			}
 
-		// top of the cube
+			if ( bits & 4 ) {
 
-		if ( bits & 1 ) {
+				compNorm( qy );
+				compNorm( q1y );
+				VIntX( qy * 3, 6, isol, fx, fy2, fz, field2, field3 );
 
-			compNorm( q );
-			compNorm( q1 );
-			VIntX( q * 3, 0, isol, fx, fy, fz, field0, field1 );
+			}
 
-		}
+			if ( bits & 8 ) {
 
-		if ( bits & 2 ) {
+				compNorm( q );
+				compNorm( qy );
+				VIntY( q * 3, 9, isol, fx, fy, fz, field0, field2 );
 
-			compNorm( q1 );
-			compNorm( q1y );
-			VIntY( q1 * 3, 3, isol, fx2, fy, fz, field1, field3 );
+			}
 
-		}
+			// bottom of the cube
 
-		if ( bits & 4 ) {
+			if ( bits & 16 ) {
 
-			compNorm( qy );
-			compNorm( q1y );
-			VIntX( qy * 3, 6, isol, fx, fy2, fz, field2, field3 );
+				compNorm( qz );
+				compNorm( q1z );
+				VIntX( qz * 3, 12, isol, fx, fy, fz2, field4, field5 );
 
-		}
+			}
 
-		if ( bits & 8 ) {
+			if ( bits & 32 ) {
 
-			compNorm( q );
-			compNorm( qy );
-			VIntY( q * 3, 9, isol, fx, fy, fz, field0, field2 );
+				compNorm( q1z );
+				compNorm( q1yz );
+				VIntY( q1z * 3, 15, isol, fx2, fy, fz2, field5, field7 );
 
-		}
+			}
 
-		// bottom of the cube
+			if ( bits & 64 ) {
 
-		if ( bits & 16 ) {
+				compNorm( qyz );
+				compNorm( q1yz );
+				VIntX( qyz * 3, 18, isol, fx, fy2, fz2, field6, field7 );
 
-			compNorm( qz );
-			compNorm( q1z );
-			VIntX( qz * 3, 12, isol, fx, fy, fz2, field4, field5 );
+			}
 
-		}
+			if ( bits & 128 ) {
 
-		if ( bits & 32 ) {
+				compNorm( qz );
+				compNorm( qyz );
+				VIntY( qz * 3, 21, isol, fx, fy, fz2, field4, field6 );
 
-			compNorm( q1z );
-			compNorm( q1yz );
-			VIntY( q1z * 3, 15, isol, fx2, fy, fz2, field5, field7 );
+			}
 
-		}
+			// vertical lines of the cube
 
-		if ( bits & 64 ) {
+			if ( bits & 256 ) {
 
-			compNorm( qyz );
-			compNorm( q1yz );
-			VIntX( qyz * 3, 18, isol, fx, fy2, fz2, field6, field7 );
+				compNorm( q );
+				compNorm( qz );
+				VIntZ( q * 3, 24, isol, fx, fy, fz, field0, field4 );
 
-		}
+			}
 
-		if ( bits & 128 ) {
+			if ( bits & 512 ) {
 
-			compNorm( qz );
-			compNorm( qyz );
-			VIntY( qz * 3, 21, isol, fx, fy, fz2, field4, field6 );
+				compNorm( q1 );
+				compNorm( q1z );
+				VIntZ( q1 * 3, 27, isol, fx2, fy, fz, field1, field5 );
 
-		}
+			}
 
-		// vertical lines of the cube
+			if ( bits & 1024 ) {
 
-		if ( bits & 256 ) {
+				compNorm( q1y );
+				compNorm( q1yz );
+				VIntZ( q1y * 3, 30, isol, fx2, fy2, fz, field3, field7 );
 
-			compNorm( q );
-			compNorm( qz );
-			VIntZ( q * 3, 24, isol, fx, fy, fz, field0, field4 );
+			}
 
-		}
+			if ( bits & 2048 ) {
 
-		if ( bits & 512 ) {
+				compNorm( qy );
+				compNorm( qyz );
+				VIntZ( qy * 3, 33, isol, fx, fy2, fz, field2, field6 );
 
-			compNorm( q1 );
-			compNorm( q1z );
-			VIntZ( q1 * 3, 27, isol, fx2, fy, fz, field1, field5 );
+			}
 
-		}
+			cubeindex <<= 4; // re-purpose cubeindex into an offset into triTable
 
-		if ( bits & 1024 ) {
+			var o1, o2, o3, numtris = 0, i = 0;
 
-			compNorm( q1y );
-			compNorm( q1yz );
-			VIntZ( q1y * 3, 30, isol, fx2, fy2, fz, field3, field7 );
+			// here is where triangles are created
 
-		}
+			while ( TriTable[ cubeindex + i ] != - 1 ) {
+
+				o1 = cubeindex + i;
+				o2 = o1 + 1;
+				o3 = o1 + 2;
 
-		if ( bits & 2048 ) {
+				posnormtriv( vlist, nlist,
+					3 * TriTable[ o1 ],
+					3 * TriTable[ o2 ],
+					3 * TriTable[ o3 ],
+					renderCallback );
 
-			compNorm( qy );
-			compNorm( qyz );
-			VIntZ( qy * 3, 33, isol, fx, fy2, fz, field2, field6 );
+				i += 3;
+				numtris ++;
+
+			}
+
+			return numtris;
 
 		}
 
-		cubeindex <<= 4; // re-purpose cubeindex into an offset into triTable
+		/////////////////////////////////////
+		// Immediate render mode simulator
+		/////////////////////////////////////
 
-		var o1, o2, o3, numtris = 0, i = 0;
+		function posnormtriv( pos, norm, o1, o2, o3, renderCallback ) {
 
-		// here is where triangles are created
+			var c = scope.count * 3;
 
-		while ( THREE.triTable[ cubeindex + i ] != - 1 ) {
+			// positions
 
-			o1 = cubeindex + i;
-			o2 = o1 + 1;
-			o3 = o1 + 2;
+			scope.positionArray[ c + 0 ] = pos[ o1 ];
+			scope.positionArray[ c + 1 ] = pos[ o1 + 1 ];
+			scope.positionArray[ c + 2 ] = pos[ o1 + 2 ];
 
-			posnormtriv( vlist, nlist,
-				3 * THREE.triTable[ o1 ],
-				3 * THREE.triTable[ o2 ],
-				3 * THREE.triTable[ o3 ],
-				renderCallback );
+			scope.positionArray[ c + 3 ] = pos[ o2 ];
+			scope.positionArray[ c + 4 ] = pos[ o2 + 1 ];
+			scope.positionArray[ c + 5 ] = pos[ o2 + 2 ];
 
-			i += 3;
-			numtris ++;
+			scope.positionArray[ c + 6 ] = pos[ o3 ];
+			scope.positionArray[ c + 7 ] = pos[ o3 + 1 ];
+			scope.positionArray[ c + 8 ] = pos[ o3 + 2 ];
 
-		}
+			// normals
 
-		return numtris;
+			if ( scope.material.flatShading === true ) {
 
-	}
+				var nx = ( norm[ o1 + 0 ] + norm[ o2 + 0 ] + norm[ o3 + 0 ] ) / 3;
+				var ny = ( norm[ o1 + 1 ] + norm[ o2 + 1 ] + norm[ o3 + 1 ] ) / 3;
+				var nz = ( norm[ o1 + 2 ] + norm[ o2 + 2 ] + norm[ o3 + 2 ] ) / 3;
 
-	/////////////////////////////////////
-	// Immediate render mode simulator
-	/////////////////////////////////////
+				scope.normalArray[ c + 0 ] = nx;
+				scope.normalArray[ c + 1 ] = ny;
+				scope.normalArray[ c + 2 ] = nz;
 
-	function posnormtriv( pos, norm, o1, o2, o3, renderCallback ) {
+				scope.normalArray[ c + 3 ] = nx;
+				scope.normalArray[ c + 4 ] = ny;
+				scope.normalArray[ c + 5 ] = nz;
 
-		var c = scope.count * 3;
+				scope.normalArray[ c + 6 ] = nx;
+				scope.normalArray[ c + 7 ] = ny;
+				scope.normalArray[ c + 8 ] = nz;
 
-		// positions
 
-		scope.positionArray[ c + 0 ] = pos[ o1 ];
-		scope.positionArray[ c + 1 ] = pos[ o1 + 1 ];
-		scope.positionArray[ c + 2 ] = pos[ o1 + 2 ];
+			} else {
 
-		scope.positionArray[ c + 3 ] = pos[ o2 ];
-		scope.positionArray[ c + 4 ] = pos[ o2 + 1 ];
-		scope.positionArray[ c + 5 ] = pos[ o2 + 2 ];
+				scope.normalArray[ c + 0 ] = norm[ o1 + 0 ];
+				scope.normalArray[ c + 1 ] = norm[ o1 + 1 ];
+				scope.normalArray[ c + 2 ] = norm[ o1 + 2 ];
 
-		scope.positionArray[ c + 6 ] = pos[ o3 ];
-		scope.positionArray[ c + 7 ] = pos[ o3 + 1 ];
-		scope.positionArray[ c + 8 ] = pos[ o3 + 2 ];
+				scope.normalArray[ c + 3 ] = norm[ o2 + 0 ];
+				scope.normalArray[ c + 4 ] = norm[ o2 + 1 ];
+				scope.normalArray[ c + 5 ] = norm[ o2 + 2 ];
 
-		// normals
+				scope.normalArray[ c + 6 ] = norm[ o3 + 0 ];
+				scope.normalArray[ c + 7 ] = norm[ o3 + 1 ];
+				scope.normalArray[ c + 8 ] = norm[ o3 + 2 ];
 
-		scope.normalArray[ c + 0 ] = norm[ o1 ];
-		scope.normalArray[ c + 1 ] = norm[ o1 + 1 ];
-		scope.normalArray[ c + 2 ] = norm[ o1 + 2 ];
+			}
 
-		scope.normalArray[ c + 3 ] = norm[ o2 ];
-		scope.normalArray[ c + 4 ] = norm[ o2 + 1 ];
-		scope.normalArray[ c + 5 ] = norm[ o2 + 2 ];
+			// uvs
 
-		scope.normalArray[ c + 6 ] = norm[ o3 ];
-		scope.normalArray[ c + 7 ] = norm[ o3 + 1 ];
-		scope.normalArray[ c + 8 ] = norm[ o3 + 2 ];
+			if ( scope.enableUvs ) {
 
-		// uvs
+				var d = scope.count * 2;
 
-		if ( scope.enableUvs ) {
+				scope.uvArray[ d + 0 ] = pos[ o1 + 0 ];
+				scope.uvArray[ d + 1 ] = pos[ o1 + 2 ];
 
-			var d = scope.count * 2;
+				scope.uvArray[ d + 2 ] = pos[ o2 + 0 ];
+				scope.uvArray[ d + 3 ] = pos[ o2 + 2 ];
 
-			scope.uvArray[ d + 0 ] = pos[ o1 ];
-			scope.uvArray[ d + 1 ] = pos[ o1 + 2 ];
+				scope.uvArray[ d + 4 ] = pos[ o3 + 0 ];
+				scope.uvArray[ d + 5 ] = pos[ o3 + 2 ];
 
-			scope.uvArray[ d + 2 ] = pos[ o2 ];
-			scope.uvArray[ d + 3 ] = pos[ o2 + 2 ];
+			}
 
-			scope.uvArray[ d + 4 ] = pos[ o3 ];
-			scope.uvArray[ d + 5 ] = pos[ o3 + 2 ];
+			// colors
 
-		}
+			if ( scope.enableColors ) {
 
-		// colors
+				scope.colorArray[ c + 0 ] = pos[ o1 + 0 ];
+				scope.colorArray[ c + 1 ] = pos[ o1 + 1 ];
+				scope.colorArray[ c + 2 ] = pos[ o1 + 2 ];
 
-		if ( scope.enableColors ) {
+				scope.colorArray[ c + 3 ] = pos[ o2 + 0 ];
+				scope.colorArray[ c + 4 ] = pos[ o2 + 1 ];
+				scope.colorArray[ c + 5 ] = pos[ o2 + 2 ];
 
-			scope.colorArray[ c + 0 ] = pos[ o1 ];
-			scope.colorArray[ c + 1 ] = pos[ o1 + 1 ];
-			scope.colorArray[ c + 2 ] = pos[ o1 + 2 ];
+				scope.colorArray[ c + 6 ] = pos[ o3 + 0 ];
+				scope.colorArray[ c + 7 ] = pos[ o3 + 1 ];
+				scope.colorArray[ c + 8 ] = pos[ o3 + 2 ];
 
-			scope.colorArray[ c + 3 ] = pos[ o2 ];
-			scope.colorArray[ c + 4 ] = pos[ o2 + 1 ];
-			scope.colorArray[ c + 5 ] = pos[ o2 + 2 ];
+			}
 
-			scope.colorArray[ c + 6 ] = pos[ o3 ];
-			scope.colorArray[ c + 7 ] = pos[ o3 + 1 ];
-			scope.colorArray[ c + 8 ] = pos[ o3 + 2 ];
+			scope.count += 3;
 
-		}
+			if ( scope.count >= scope.maxCount - 3 ) {
 
-		scope.count += 3;
+				scope.hasPositions = true;
+				scope.hasNormals = true;
 
-		if ( scope.count >= scope.maxCount - 3 ) {
+				if ( scope.enableUvs ) {
 
-			scope.hasPositions = true;
-			scope.hasNormals = true;
+					scope.hasUvs = true;
 
-			if ( scope.enableUvs ) {
+				}
 
-				scope.hasUvs = true;
+				if ( scope.enableColors ) {
 
-			}
+					scope.hasColors = true;
 
-			if ( scope.enableColors ) {
+				}
 
-				scope.hasColors = true;
+				renderCallback( scope );
 
 			}
 
-			renderCallback( scope );
-
 		}
 
-	}
+		function begin() {
 
-	this.begin = function () {
+			scope.count = 0;
 
-		this.count = 0;
+			scope.hasPositions = false;
+			scope.hasNormals = false;
+			scope.hasUvs = false;
+			scope.hasColors = false;
 
-		this.hasPositions = false;
-		this.hasNormals = false;
-		this.hasUvs = false;
-		this.hasColors = false;
+		}
 
-	};
+		function end( renderCallback ) {
 
-	this.end = function ( renderCallback ) {
+			if ( scope.count === 0 ) return;
 
-		if ( this.count === 0 ) return;
+			for ( var i = scope.count * 3; i < scope.positionArray.length; i ++ ) {
 
-		for ( var i = this.count * 3; i < this.positionArray.length; i ++ ) {
+				scope.positionArray[ i ] = 0.0;
 
-			this.positionArray[ i ] = 0.0;
+			}
 
-		}
+			scope.hasPositions = true;
+			scope.hasNormals = true;
 
-		this.hasPositions = true;
-		this.hasNormals = true;
+			if ( scope.enableUvs && scope.material.map ) {
 
-		if ( this.enableUvs ) {
+				scope.hasUvs = true;
 
-			this.hasUvs = true;
+			}
 
-		}
+			if ( scope.enableColors && scope.material.vertexColors !== THREE.NoColors ) {
 
-		if ( this.enableColors ) {
+				scope.hasColors = true;
 
-			this.hasColors = true;
+			}
 
-		}
+			renderCallback( scope );
 
-		renderCallback( this );
+		}
 
-	};
+		/////////////////////////////////////
+		// Metaballs
+		/////////////////////////////////////
 
-	/////////////////////////////////////
-	// Metaballs
-	/////////////////////////////////////
+		// Adds a reciprocal ball (nice and blobby) that, to be fast, fades to zero after
+		// a fixed distance, determined by strength and subtract.
 
-	// Adds a reciprocal ball (nice and blobby) that, to be fast, fades to zero after
-	// a fixed distance, determined by strength and subtract.
+		this.addBall = function ( ballx, bally, ballz, strength, subtract ) {
 
-	this.addBall = function ( ballx, bally, ballz, strength, subtract ) {
+			var sign = Math.sign( strength );
+			strength = Math.abs( strength );
 
-		var sign = Math.sign( strength );
-		strength = Math.abs( strength );
+			// Let's solve the equation to find the radius:
+			// 1.0 / (0.000001 + radius^2) * strength - subtract = 0
+			// strength / (radius^2) = subtract
+			// strength = subtract * radius^2
+			// radius^2 = strength / subtract
+			// radius = sqrt(strength / subtract)
 
-		// Let's solve the equation to find the radius:
-		// 1.0 / (0.000001 + radius^2) * strength - subtract = 0
-		// strength / (radius^2) = subtract
-		// strength = subtract * radius^2
-		// radius^2 = strength / subtract
-		// radius = sqrt(strength / subtract)
+			var radius = this.size * Math.sqrt( strength / subtract ),
+				zs = ballz * this.size,
+				ys = bally * this.size,
+				xs = ballx * this.size;
 
-		var radius = this.size * Math.sqrt( strength / subtract ),
-			zs = ballz * this.size,
-			ys = bally * this.size,
-			xs = ballx * this.size;
+			var min_z = Math.floor( zs - radius ); if ( min_z < 1 ) min_z = 1;
+			var max_z = Math.floor( zs + radius ); if ( max_z > this.size - 1 ) max_z = this.size - 1;
+			var min_y = Math.floor( ys - radius ); if ( min_y < 1 ) min_y = 1;
+			var max_y = Math.floor( ys + radius ); if ( max_y > this.size - 1 ) max_y = this.size - 1;
+			var min_x = Math.floor( xs - radius ); if ( min_x < 1 ) min_x = 1;
+			var max_x = Math.floor( xs + radius ); if ( max_x > this.size - 1 ) max_x = this.size - 1;
 
-		var min_z = Math.floor( zs - radius ); if ( min_z < 1 ) min_z = 1;
-		var max_z = Math.floor( zs + radius ); if ( max_z > this.size - 1 ) max_z = this.size - 1;
-		var min_y = Math.floor( ys - radius ); if ( min_y < 1 ) min_y = 1;
-		var max_y = Math.floor( ys + radius ); if ( max_y > this.size - 1 ) max_y = this.size - 1;
-		var min_x = Math.floor( xs - radius ); if ( min_x < 1 ) min_x = 1;
-		var max_x = Math.floor( xs + radius ); if ( max_x > this.size - 1 ) max_x = this.size - 1;
 
+			// Don't polygonize in the outer layer because normals aren't
+			// well-defined there.
 
-		// Don't polygonize in the outer layer because normals aren't
-		// well-defined there.
+			var x, y, z, y_offset, z_offset, fx, fy, fz, fz2, fy2, val;
 
-		var x, y, z, y_offset, z_offset, fx, fy, fz, fz2, fy2, val;
+			for ( z = min_z; z < max_z; z ++ ) {
 
-		for ( z = min_z; z < max_z; z ++ ) {
+				z_offset = this.size2 * z;
+				fz = z / this.size - ballz;
+				fz2 = fz * fz;
 
-			z_offset = this.size2 * z;
-			fz = z / this.size - ballz;
-			fz2 = fz * fz;
+				for ( y = min_y; y < max_y; y ++ ) {
 
-			for ( y = min_y; y < max_y; y ++ ) {
+					y_offset = z_offset + this.size * y;
+					fy = y / this.size - bally;
+					fy2 = fy * fy;
 
-				y_offset = z_offset + this.size * y;
-				fy = y / this.size - bally;
-				fy2 = fy * fy;
+					for ( x = min_x; x < max_x; x ++ ) {
 
-				for ( x = min_x; x < max_x; x ++ ) {
+						fx = x / this.size - ballx;
+						val = strength / ( 0.000001 + fx * fx + fy2 + fz2 ) - subtract;
+						if ( val > 0.0 ) field[ y_offset + x ] += val * sign;
 
-					fx = x / this.size - ballx;
-					val = strength / ( 0.000001 + fx * fx + fy2 + fz2 ) - subtract;
-					if ( val > 0.0 ) this.field[ y_offset + x ] += val * sign;
+					}
 
 				}
 
 			}
 
-		}
+		};
 
-	};
+		this.addPlaneX = function ( strength, subtract ) {
 
-	this.addPlaneX = function ( strength, subtract ) {
+			var x, y, z, xx, val, xdiv, cxy,
 
-		var x, y, z, xx, val, xdiv, cxy,
+				// cache attribute lookups
+				size = this.size,
 
-			// cache attribute lookups
-			size = this.size,
-			yd = this.yd,
-			zd = this.zd,
-			field = this.field,
+				dist = size * Math.sqrt( strength / subtract );
 
-			dist = size * Math.sqrt( strength / subtract );
+			if ( dist > size ) dist = size;
 
-		if ( dist > size ) dist = size;
+			for ( x = 0; x < dist; x ++ ) {
 
-		for ( x = 0; x < dist; x ++ ) {
+				xdiv = x / size;
+				xx = xdiv * xdiv;
+				val = strength / ( 0.0001 + xx ) - subtract;
 
-			xdiv = x / size;
-			xx = xdiv * xdiv;
-			val = strength / ( 0.0001 + xx ) - subtract;
+				if ( val > 0.0 ) {
 
-			if ( val > 0.0 ) {
+					for ( y = 0; y < size; y ++ ) {
 
-				for ( y = 0; y < size; y ++ ) {
+						cxy = x + y * yd;
 
-					cxy = x + y * yd;
+						for ( z = 0; z < size; z ++ ) {
 
-					for ( z = 0; z < size; z ++ ) {
+							field[ zd * z + cxy ] += val;
 
-						field[ zd * z + cxy ] += val;
+						}
 
 					}
 
@@ -553,492 +578,484 @@ THREE.MarchingCubes = function ( resolution, material, enableUvs, enableColors )
 
 			}
 
-		}
+		};
 
-	};
+		this.addPlaneY = function ( strength, subtract ) {
 
-	this.addPlaneY = function ( strength, subtract ) {
+			var x, y, z, yy, val, ydiv, cy, cxy,
 
-		var x, y, z, yy, val, ydiv, cy, cxy,
+				// cache attribute lookups
+				size = this.size,
 
-			// cache attribute lookups
-			size = this.size,
-			yd = this.yd,
-			zd = this.zd,
-			field = this.field,
+				dist = size * Math.sqrt( strength / subtract );
 
-			dist = size * Math.sqrt( strength / subtract );
+			if ( dist > size ) dist = size;
 
-		if ( dist > size ) dist = size;
+			for ( y = 0; y < dist; y ++ ) {
 
-		for ( y = 0; y < dist; y ++ ) {
+				ydiv = y / size;
+				yy = ydiv * ydiv;
+				val = strength / ( 0.0001 + yy ) - subtract;
 
-			ydiv = y / size;
-			yy = ydiv * ydiv;
-			val = strength / ( 0.0001 + yy ) - subtract;
+				if ( val > 0.0 ) {
 
-			if ( val > 0.0 ) {
+					cy = y * yd;
 
-				cy = y * yd;
+					for ( x = 0; x < size; x ++ ) {
 
-				for ( x = 0; x < size; x ++ ) {
+						cxy = cy + x;
 
-					cxy = cy + x;
+						for ( z = 0; z < size; z ++ )
+							field[ zd * z + cxy ] += val;
 
-					for ( z = 0; z < size; z ++ )
-						field[ zd * z + cxy ] += val;
+					}
 
 				}
 
 			}
 
-		}
+		};
 
-	};
+		this.addPlaneZ = function ( strength, subtract ) {
 
-	this.addPlaneZ = function ( strength, subtract ) {
+			var x, y, z, zz, val, zdiv, cz, cyz,
 
-		var x, y, z, zz, val, zdiv, cz, cyz,
+				// cache attribute lookups
+				size = this.size,
 
-			// cache attribute lookups
-			size = this.size,
-			yd = this.yd,
-			zd = this.zd,
-			field = this.field,
+				dist = size * Math.sqrt( strength / subtract );
 
-			dist = size * Math.sqrt( strength / subtract );
+			if ( dist > size ) dist = size;
 
-		if ( dist > size ) dist = size;
+			for ( z = 0; z < dist; z ++ ) {
 
-		for ( z = 0; z < dist; z ++ ) {
+				zdiv = z / size;
+				zz = zdiv * zdiv;
+				val = strength / ( 0.0001 + zz ) - subtract;
+				if ( val > 0.0 ) {
 
-			zdiv = z / size;
-			zz = zdiv * zdiv;
-			val = strength / ( 0.0001 + zz ) - subtract;
-			if ( val > 0.0 ) {
+					cz = zd * z;
 
-				cz = zd * z;
+					for ( y = 0; y < size; y ++ ) {
 
-				for ( y = 0; y < size; y ++ ) {
+						cyz = cz + y * yd;
 
-					cyz = cz + y * yd;
+						for ( x = 0; x < size; x ++ )
+							field[ cyz + x ] += val;
 
-					for ( x = 0; x < size; x ++ )
-						field[ cyz + x ] += val;
+					}
 
 				}
 
 			}
 
-		}
+		};
 
-	};
+		/////////////////////////////////////
+		// Updates
+		/////////////////////////////////////
 
-	/////////////////////////////////////
-	// Updates
-	/////////////////////////////////////
+		this.reset = function () {
 
-	this.reset = function () {
+			// wipe the normal cache
 
-		var i;
+			for ( var i = 0; i < this.size3; i ++ ) {
 
-		// wipe the normal cache
+				normal_cache[ i * 3 ] = 0.0;
+				field[ i ] = 0.0;
 
-		for ( i = 0; i < this.size3; i ++ ) {
+			}
 
-			this.normal_cache[ i * 3 ] = 0.0;
-			this.field[ i ] = 0.0;
+		};
 
-		}
+		this.render = function ( renderCallback ) {
 
-	};
+			begin();
 
-	this.render = function ( renderCallback ) {
+			// Triangulate. Yeah, this is slow.
 
-		this.begin();
+			var smin2 = this.size - 2;
 
-		// Triangulate. Yeah, this is slow.
+			for ( var z = 1; z < smin2; z ++ ) {
 
-		var smin2 = this.size - 2;
+				var z_offset = this.size2 * z;
+				var fz = ( z - this.halfsize ) / this.halfsize; //+ 1
 
-		for ( var z = 1; z < smin2; z ++ ) {
+				for ( var y = 1; y < smin2; y ++ ) {
 
-			var z_offset = this.size2 * z;
-			var fz = ( z - this.halfsize ) / this.halfsize; //+ 1
+					var y_offset = z_offset + this.size * y;
+					var fy = ( y - this.halfsize ) / this.halfsize; //+ 1
 
-			for ( var y = 1; y < smin2; y ++ ) {
+					for ( var x = 1; x < smin2; x ++ ) {
 
-				var y_offset = z_offset + this.size * y;
-				var fy = ( y - this.halfsize ) / this.halfsize; //+ 1
+						var fx = ( x - this.halfsize ) / this.halfsize; //+ 1
+						var q = y_offset + x;
 
-				for ( var x = 1; x < smin2; x ++ ) {
+						polygonize( fx, fy, fz, q, this.isolation, renderCallback );
 
-					var fx = ( x - this.halfsize ) / this.halfsize; //+ 1
-					var q = y_offset + x;
-
-					polygonize( fx, fy, fz, q, this.isolation, renderCallback );
+					}
 
 				}
 
 			}
 
-		}
+			end( renderCallback );
 
-		this.end( renderCallback );
+		};
 
-	};
+		this.generateGeometry = function () {
 
-	this.generateGeometry = function () {
+			console.warn( 'THREE.MarchingCubes: generateGeometry() now returns THREE.BufferGeometry' );
+			return this.generateBufferGeometry();
 
-		console.warn( 'THREE.MarchingCubes: generateGeometry() now returns THREE.BufferGeometry' );
-		return this.generateBufferGeometry();
+		};
 
-	};
+		function concatenate( a, b, length ) {
 
-	function concatenate( a, b, length ) {
+			var result = new Float32Array( a.length + length );
+			result.set( a, 0 );
+			result.set( b.slice( 0, length ), a.length );
+			return result;
 
-		var result = new Float32Array( a.length + length );
-		result.set( a, 0 );
-		result.set( b.slice( 0, length ), a.length );
-		return result;
+		}
 
-	}
+		this.generateBufferGeometry = function () {
 
-	this.generateBufferGeometry = function () {
+			var geo = new THREE.BufferGeometry();
+			var posArray = new Float32Array();
+			var normArray = new Float32Array();
+			var colorArray = new Float32Array();
+			var uvArray = new Float32Array();
+			var scope = this;
 
-		var geo = new THREE.BufferGeometry();
-		var posArray = new Float32Array();
-		var normArray = new Float32Array();
-		var colorArray = new Float32Array();
-		var uvArray = new Float32Array();
-		var scope = this;
+			var geo_callback = function ( object ) {
 
-		var geo_callback = function ( object ) {
+				if ( scope.hasPositions ) posArray = concatenate( posArray, object.positionArray, object.count * 3 );
+				if ( scope.hasNormals ) normArray = concatenate( normArray, object.normalArray, object.count * 3 );
+				if ( scope.hasColors ) colorArray = concatenate( colorArray, object.colorArray, object.count * 3 );
+				if ( scope.hasUvs ) uvArray = concatenate( uvArray, object.uvArray, object.count * 2 );
 
-			if ( scope.hasPositions ) posArray = concatenate( posArray, object.positionArray, object.count * 3 );
-			if ( scope.hasNormals ) normArray = concatenate( normArray, object.normalArray, object.count * 3 );
-			if ( scope.hasColors ) colorArray = concatenate( colorArray, object.colorArray, object.count * 3 );
-			if ( scope.hasUvs ) uvArray = concatenate( uvArray, object.uvArray, object.count * 2 );
+				object.count = 0;
 
-			object.count = 0;
+			};
 
-		};
+			this.render( geo_callback );
 
-		this.render( geo_callback );
+			if ( this.hasPositions ) geo.addAttribute( 'position', new THREE.BufferAttribute( posArray, 3 ) );
+			if ( this.hasNormals ) geo.addAttribute( 'normal', new THREE.BufferAttribute( normArray, 3 ) );
+			if ( this.hasColors ) geo.addAttribute( 'color', new THREE.BufferAttribute( colorArray, 3 ) );
+			if ( this.hasUvs ) geo.addAttribute( 'uv', new THREE.BufferAttribute( uvArray, 2 ) );
 
-		if ( this.hasPositions ) geo.addAttribute( 'position', new THREE.BufferAttribute( posArray, 3 ) );
-		if ( this.hasNormals ) geo.addAttribute( 'normal', new THREE.BufferAttribute( normArray, 3 ) );
-		if ( this.hasColors ) geo.addAttribute( 'color', new THREE.BufferAttribute( colorArray, 3 ) );
-		if ( this.hasUvs ) geo.addAttribute( 'uv', new THREE.BufferAttribute( uvArray, 2 ) );
+			return geo;
 
-		return geo;
+		};
+
+		this.init( resolution );
 
 	};
 
-	this.init( resolution );
-
-};
-
-THREE.MarchingCubes.prototype = Object.create( THREE.ImmediateRenderObject.prototype );
-THREE.MarchingCubes.prototype.constructor = THREE.MarchingCubes;
-
-
-/////////////////////////////////////
-// Marching cubes lookup tables
-/////////////////////////////////////
-
-// These tables are straight from Paul Bourke's page:
-// http://local.wasp.uwa.edu.au/~pbourke/geometry/polygonise/
-// who in turn got them from Cory Gene Bloyd.
-
-THREE.edgeTable = new Int32Array( [
-	0x0, 0x109, 0x203, 0x30a, 0x406, 0x50f, 0x605, 0x70c,
-	0x80c, 0x905, 0xa0f, 0xb06, 0xc0a, 0xd03, 0xe09, 0xf00,
-	0x190, 0x99, 0x393, 0x29a, 0x596, 0x49f, 0x795, 0x69c,
-	0x99c, 0x895, 0xb9f, 0xa96, 0xd9a, 0xc93, 0xf99, 0xe90,
-	0x230, 0x339, 0x33, 0x13a, 0x636, 0x73f, 0x435, 0x53c,
-	0xa3c, 0xb35, 0x83f, 0x936, 0xe3a, 0xf33, 0xc39, 0xd30,
-	0x3a0, 0x2a9, 0x1a3, 0xaa, 0x7a6, 0x6af, 0x5a5, 0x4ac,
-	0xbac, 0xaa5, 0x9af, 0x8a6, 0xfaa, 0xea3, 0xda9, 0xca0,
-	0x460, 0x569, 0x663, 0x76a, 0x66, 0x16f, 0x265, 0x36c,
-	0xc6c, 0xd65, 0xe6f, 0xf66, 0x86a, 0x963, 0xa69, 0xb60,
-	0x5f0, 0x4f9, 0x7f3, 0x6fa, 0x1f6, 0xff, 0x3f5, 0x2fc,
-	0xdfc, 0xcf5, 0xfff, 0xef6, 0x9fa, 0x8f3, 0xbf9, 0xaf0,
-	0x650, 0x759, 0x453, 0x55a, 0x256, 0x35f, 0x55, 0x15c,
-	0xe5c, 0xf55, 0xc5f, 0xd56, 0xa5a, 0xb53, 0x859, 0x950,
-	0x7c0, 0x6c9, 0x5c3, 0x4ca, 0x3c6, 0x2cf, 0x1c5, 0xcc,
-	0xfcc, 0xec5, 0xdcf, 0xcc6, 0xbca, 0xac3, 0x9c9, 0x8c0,
-	0x8c0, 0x9c9, 0xac3, 0xbca, 0xcc6, 0xdcf, 0xec5, 0xfcc,
-	0xcc, 0x1c5, 0x2cf, 0x3c6, 0x4ca, 0x5c3, 0x6c9, 0x7c0,
-	0x950, 0x859, 0xb53, 0xa5a, 0xd56, 0xc5f, 0xf55, 0xe5c,
-	0x15c, 0x55, 0x35f, 0x256, 0x55a, 0x453, 0x759, 0x650,
-	0xaf0, 0xbf9, 0x8f3, 0x9fa, 0xef6, 0xfff, 0xcf5, 0xdfc,
-	0x2fc, 0x3f5, 0xff, 0x1f6, 0x6fa, 0x7f3, 0x4f9, 0x5f0,
-	0xb60, 0xa69, 0x963, 0x86a, 0xf66, 0xe6f, 0xd65, 0xc6c,
-	0x36c, 0x265, 0x16f, 0x66, 0x76a, 0x663, 0x569, 0x460,
-	0xca0, 0xda9, 0xea3, 0xfaa, 0x8a6, 0x9af, 0xaa5, 0xbac,
-	0x4ac, 0x5a5, 0x6af, 0x7a6, 0xaa, 0x1a3, 0x2a9, 0x3a0,
-	0xd30, 0xc39, 0xf33, 0xe3a, 0x936, 0x83f, 0xb35, 0xa3c,
-	0x53c, 0x435, 0x73f, 0x636, 0x13a, 0x33, 0x339, 0x230,
-	0xe90, 0xf99, 0xc93, 0xd9a, 0xa96, 0xb9f, 0x895, 0x99c,
-	0x69c, 0x795, 0x49f, 0x596, 0x29a, 0x393, 0x99, 0x190,
-	0xf00, 0xe09, 0xd03, 0xc0a, 0xb06, 0xa0f, 0x905, 0x80c,
-	0x70c, 0x605, 0x50f, 0x406, 0x30a, 0x203, 0x109, 0x0 ] );
-
-THREE.triTable = new Int32Array( [
-	- 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	0, 8, 3, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	0, 1, 9, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	1, 8, 3, 9, 8, 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	1, 2, 10, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	0, 8, 3, 1, 2, 10, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	9, 2, 10, 0, 2, 9, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	2, 8, 3, 2, 10, 8, 10, 9, 8, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	3, 11, 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	0, 11, 2, 8, 11, 0, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	1, 9, 0, 2, 3, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	1, 11, 2, 1, 9, 11, 9, 8, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	3, 10, 1, 11, 10, 3, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	0, 10, 1, 0, 8, 10, 8, 11, 10, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	3, 9, 0, 3, 11, 9, 11, 10, 9, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	9, 8, 10, 10, 8, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	4, 7, 8, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	4, 3, 0, 7, 3, 4, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	0, 1, 9, 8, 4, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	4, 1, 9, 4, 7, 1, 7, 3, 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	1, 2, 10, 8, 4, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	3, 4, 7, 3, 0, 4, 1, 2, 10, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	9, 2, 10, 9, 0, 2, 8, 4, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	2, 10, 9, 2, 9, 7, 2, 7, 3, 7, 9, 4, - 1, - 1, - 1, - 1,
-	8, 4, 7, 3, 11, 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	11, 4, 7, 11, 2, 4, 2, 0, 4, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	9, 0, 1, 8, 4, 7, 2, 3, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	4, 7, 11, 9, 4, 11, 9, 11, 2, 9, 2, 1, - 1, - 1, - 1, - 1,
-	3, 10, 1, 3, 11, 10, 7, 8, 4, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	1, 11, 10, 1, 4, 11, 1, 0, 4, 7, 11, 4, - 1, - 1, - 1, - 1,
-	4, 7, 8, 9, 0, 11, 9, 11, 10, 11, 0, 3, - 1, - 1, - 1, - 1,
-	4, 7, 11, 4, 11, 9, 9, 11, 10, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	9, 5, 4, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	9, 5, 4, 0, 8, 3, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	0, 5, 4, 1, 5, 0, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	8, 5, 4, 8, 3, 5, 3, 1, 5, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	1, 2, 10, 9, 5, 4, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	3, 0, 8, 1, 2, 10, 4, 9, 5, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	5, 2, 10, 5, 4, 2, 4, 0, 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	2, 10, 5, 3, 2, 5, 3, 5, 4, 3, 4, 8, - 1, - 1, - 1, - 1,
-	9, 5, 4, 2, 3, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	0, 11, 2, 0, 8, 11, 4, 9, 5, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	0, 5, 4, 0, 1, 5, 2, 3, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	2, 1, 5, 2, 5, 8, 2, 8, 11, 4, 8, 5, - 1, - 1, - 1, - 1,
-	10, 3, 11, 10, 1, 3, 9, 5, 4, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	4, 9, 5, 0, 8, 1, 8, 10, 1, 8, 11, 10, - 1, - 1, - 1, - 1,
-	5, 4, 0, 5, 0, 11, 5, 11, 10, 11, 0, 3, - 1, - 1, - 1, - 1,
-	5, 4, 8, 5, 8, 10, 10, 8, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	9, 7, 8, 5, 7, 9, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	9, 3, 0, 9, 5, 3, 5, 7, 3, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	0, 7, 8, 0, 1, 7, 1, 5, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	1, 5, 3, 3, 5, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	9, 7, 8, 9, 5, 7, 10, 1, 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	10, 1, 2, 9, 5, 0, 5, 3, 0, 5, 7, 3, - 1, - 1, - 1, - 1,
-	8, 0, 2, 8, 2, 5, 8, 5, 7, 10, 5, 2, - 1, - 1, - 1, - 1,
-	2, 10, 5, 2, 5, 3, 3, 5, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	7, 9, 5, 7, 8, 9, 3, 11, 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	9, 5, 7, 9, 7, 2, 9, 2, 0, 2, 7, 11, - 1, - 1, - 1, - 1,
-	2, 3, 11, 0, 1, 8, 1, 7, 8, 1, 5, 7, - 1, - 1, - 1, - 1,
-	11, 2, 1, 11, 1, 7, 7, 1, 5, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	9, 5, 8, 8, 5, 7, 10, 1, 3, 10, 3, 11, - 1, - 1, - 1, - 1,
-	5, 7, 0, 5, 0, 9, 7, 11, 0, 1, 0, 10, 11, 10, 0, - 1,
-	11, 10, 0, 11, 0, 3, 10, 5, 0, 8, 0, 7, 5, 7, 0, - 1,
-	11, 10, 5, 7, 11, 5, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	10, 6, 5, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	0, 8, 3, 5, 10, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	9, 0, 1, 5, 10, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	1, 8, 3, 1, 9, 8, 5, 10, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	1, 6, 5, 2, 6, 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	1, 6, 5, 1, 2, 6, 3, 0, 8, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	9, 6, 5, 9, 0, 6, 0, 2, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	5, 9, 8, 5, 8, 2, 5, 2, 6, 3, 2, 8, - 1, - 1, - 1, - 1,
-	2, 3, 11, 10, 6, 5, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	11, 0, 8, 11, 2, 0, 10, 6, 5, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	0, 1, 9, 2, 3, 11, 5, 10, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	5, 10, 6, 1, 9, 2, 9, 11, 2, 9, 8, 11, - 1, - 1, - 1, - 1,
-	6, 3, 11, 6, 5, 3, 5, 1, 3, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	0, 8, 11, 0, 11, 5, 0, 5, 1, 5, 11, 6, - 1, - 1, - 1, - 1,
-	3, 11, 6, 0, 3, 6, 0, 6, 5, 0, 5, 9, - 1, - 1, - 1, - 1,
-	6, 5, 9, 6, 9, 11, 11, 9, 8, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	5, 10, 6, 4, 7, 8, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	4, 3, 0, 4, 7, 3, 6, 5, 10, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	1, 9, 0, 5, 10, 6, 8, 4, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	10, 6, 5, 1, 9, 7, 1, 7, 3, 7, 9, 4, - 1, - 1, - 1, - 1,
-	6, 1, 2, 6, 5, 1, 4, 7, 8, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	1, 2, 5, 5, 2, 6, 3, 0, 4, 3, 4, 7, - 1, - 1, - 1, - 1,
-	8, 4, 7, 9, 0, 5, 0, 6, 5, 0, 2, 6, - 1, - 1, - 1, - 1,
-	7, 3, 9, 7, 9, 4, 3, 2, 9, 5, 9, 6, 2, 6, 9, - 1,
-	3, 11, 2, 7, 8, 4, 10, 6, 5, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	5, 10, 6, 4, 7, 2, 4, 2, 0, 2, 7, 11, - 1, - 1, - 1, - 1,
-	0, 1, 9, 4, 7, 8, 2, 3, 11, 5, 10, 6, - 1, - 1, - 1, - 1,
-	9, 2, 1, 9, 11, 2, 9, 4, 11, 7, 11, 4, 5, 10, 6, - 1,
-	8, 4, 7, 3, 11, 5, 3, 5, 1, 5, 11, 6, - 1, - 1, - 1, - 1,
-	5, 1, 11, 5, 11, 6, 1, 0, 11, 7, 11, 4, 0, 4, 11, - 1,
-	0, 5, 9, 0, 6, 5, 0, 3, 6, 11, 6, 3, 8, 4, 7, - 1,
-	6, 5, 9, 6, 9, 11, 4, 7, 9, 7, 11, 9, - 1, - 1, - 1, - 1,
-	10, 4, 9, 6, 4, 10, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	4, 10, 6, 4, 9, 10, 0, 8, 3, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	10, 0, 1, 10, 6, 0, 6, 4, 0, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	8, 3, 1, 8, 1, 6, 8, 6, 4, 6, 1, 10, - 1, - 1, - 1, - 1,
-	1, 4, 9, 1, 2, 4, 2, 6, 4, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	3, 0, 8, 1, 2, 9, 2, 4, 9, 2, 6, 4, - 1, - 1, - 1, - 1,
-	0, 2, 4, 4, 2, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	8, 3, 2, 8, 2, 4, 4, 2, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	10, 4, 9, 10, 6, 4, 11, 2, 3, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	0, 8, 2, 2, 8, 11, 4, 9, 10, 4, 10, 6, - 1, - 1, - 1, - 1,
-	3, 11, 2, 0, 1, 6, 0, 6, 4, 6, 1, 10, - 1, - 1, - 1, - 1,
-	6, 4, 1, 6, 1, 10, 4, 8, 1, 2, 1, 11, 8, 11, 1, - 1,
-	9, 6, 4, 9, 3, 6, 9, 1, 3, 11, 6, 3, - 1, - 1, - 1, - 1,
-	8, 11, 1, 8, 1, 0, 11, 6, 1, 9, 1, 4, 6, 4, 1, - 1,
-	3, 11, 6, 3, 6, 0, 0, 6, 4, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	6, 4, 8, 11, 6, 8, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	7, 10, 6, 7, 8, 10, 8, 9, 10, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	0, 7, 3, 0, 10, 7, 0, 9, 10, 6, 7, 10, - 1, - 1, - 1, - 1,
-	10, 6, 7, 1, 10, 7, 1, 7, 8, 1, 8, 0, - 1, - 1, - 1, - 1,
-	10, 6, 7, 10, 7, 1, 1, 7, 3, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	1, 2, 6, 1, 6, 8, 1, 8, 9, 8, 6, 7, - 1, - 1, - 1, - 1,
-	2, 6, 9, 2, 9, 1, 6, 7, 9, 0, 9, 3, 7, 3, 9, - 1,
-	7, 8, 0, 7, 0, 6, 6, 0, 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	7, 3, 2, 6, 7, 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	2, 3, 11, 10, 6, 8, 10, 8, 9, 8, 6, 7, - 1, - 1, - 1, - 1,
-	2, 0, 7, 2, 7, 11, 0, 9, 7, 6, 7, 10, 9, 10, 7, - 1,
-	1, 8, 0, 1, 7, 8, 1, 10, 7, 6, 7, 10, 2, 3, 11, - 1,
-	11, 2, 1, 11, 1, 7, 10, 6, 1, 6, 7, 1, - 1, - 1, - 1, - 1,
-	8, 9, 6, 8, 6, 7, 9, 1, 6, 11, 6, 3, 1, 3, 6, - 1,
-	0, 9, 1, 11, 6, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	7, 8, 0, 7, 0, 6, 3, 11, 0, 11, 6, 0, - 1, - 1, - 1, - 1,
-	7, 11, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	7, 6, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	3, 0, 8, 11, 7, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	0, 1, 9, 11, 7, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	8, 1, 9, 8, 3, 1, 11, 7, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	10, 1, 2, 6, 11, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	1, 2, 10, 3, 0, 8, 6, 11, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	2, 9, 0, 2, 10, 9, 6, 11, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	6, 11, 7, 2, 10, 3, 10, 8, 3, 10, 9, 8, - 1, - 1, - 1, - 1,
-	7, 2, 3, 6, 2, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	7, 0, 8, 7, 6, 0, 6, 2, 0, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	2, 7, 6, 2, 3, 7, 0, 1, 9, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	1, 6, 2, 1, 8, 6, 1, 9, 8, 8, 7, 6, - 1, - 1, - 1, - 1,
-	10, 7, 6, 10, 1, 7, 1, 3, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	10, 7, 6, 1, 7, 10, 1, 8, 7, 1, 0, 8, - 1, - 1, - 1, - 1,
-	0, 3, 7, 0, 7, 10, 0, 10, 9, 6, 10, 7, - 1, - 1, - 1, - 1,
-	7, 6, 10, 7, 10, 8, 8, 10, 9, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	6, 8, 4, 11, 8, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	3, 6, 11, 3, 0, 6, 0, 4, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	8, 6, 11, 8, 4, 6, 9, 0, 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	9, 4, 6, 9, 6, 3, 9, 3, 1, 11, 3, 6, - 1, - 1, - 1, - 1,
-	6, 8, 4, 6, 11, 8, 2, 10, 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	1, 2, 10, 3, 0, 11, 0, 6, 11, 0, 4, 6, - 1, - 1, - 1, - 1,
-	4, 11, 8, 4, 6, 11, 0, 2, 9, 2, 10, 9, - 1, - 1, - 1, - 1,
-	10, 9, 3, 10, 3, 2, 9, 4, 3, 11, 3, 6, 4, 6, 3, - 1,
-	8, 2, 3, 8, 4, 2, 4, 6, 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	0, 4, 2, 4, 6, 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	1, 9, 0, 2, 3, 4, 2, 4, 6, 4, 3, 8, - 1, - 1, - 1, - 1,
-	1, 9, 4, 1, 4, 2, 2, 4, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	8, 1, 3, 8, 6, 1, 8, 4, 6, 6, 10, 1, - 1, - 1, - 1, - 1,
-	10, 1, 0, 10, 0, 6, 6, 0, 4, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	4, 6, 3, 4, 3, 8, 6, 10, 3, 0, 3, 9, 10, 9, 3, - 1,
-	10, 9, 4, 6, 10, 4, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	4, 9, 5, 7, 6, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	0, 8, 3, 4, 9, 5, 11, 7, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	5, 0, 1, 5, 4, 0, 7, 6, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	11, 7, 6, 8, 3, 4, 3, 5, 4, 3, 1, 5, - 1, - 1, - 1, - 1,
-	9, 5, 4, 10, 1, 2, 7, 6, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	6, 11, 7, 1, 2, 10, 0, 8, 3, 4, 9, 5, - 1, - 1, - 1, - 1,
-	7, 6, 11, 5, 4, 10, 4, 2, 10, 4, 0, 2, - 1, - 1, - 1, - 1,
-	3, 4, 8, 3, 5, 4, 3, 2, 5, 10, 5, 2, 11, 7, 6, - 1,
-	7, 2, 3, 7, 6, 2, 5, 4, 9, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	9, 5, 4, 0, 8, 6, 0, 6, 2, 6, 8, 7, - 1, - 1, - 1, - 1,
-	3, 6, 2, 3, 7, 6, 1, 5, 0, 5, 4, 0, - 1, - 1, - 1, - 1,
-	6, 2, 8, 6, 8, 7, 2, 1, 8, 4, 8, 5, 1, 5, 8, - 1,
-	9, 5, 4, 10, 1, 6, 1, 7, 6, 1, 3, 7, - 1, - 1, - 1, - 1,
-	1, 6, 10, 1, 7, 6, 1, 0, 7, 8, 7, 0, 9, 5, 4, - 1,
-	4, 0, 10, 4, 10, 5, 0, 3, 10, 6, 10, 7, 3, 7, 10, - 1,
-	7, 6, 10, 7, 10, 8, 5, 4, 10, 4, 8, 10, - 1, - 1, - 1, - 1,
-	6, 9, 5, 6, 11, 9, 11, 8, 9, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	3, 6, 11, 0, 6, 3, 0, 5, 6, 0, 9, 5, - 1, - 1, - 1, - 1,
-	0, 11, 8, 0, 5, 11, 0, 1, 5, 5, 6, 11, - 1, - 1, - 1, - 1,
-	6, 11, 3, 6, 3, 5, 5, 3, 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	1, 2, 10, 9, 5, 11, 9, 11, 8, 11, 5, 6, - 1, - 1, - 1, - 1,
-	0, 11, 3, 0, 6, 11, 0, 9, 6, 5, 6, 9, 1, 2, 10, - 1,
-	11, 8, 5, 11, 5, 6, 8, 0, 5, 10, 5, 2, 0, 2, 5, - 1,
-	6, 11, 3, 6, 3, 5, 2, 10, 3, 10, 5, 3, - 1, - 1, - 1, - 1,
-	5, 8, 9, 5, 2, 8, 5, 6, 2, 3, 8, 2, - 1, - 1, - 1, - 1,
-	9, 5, 6, 9, 6, 0, 0, 6, 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	1, 5, 8, 1, 8, 0, 5, 6, 8, 3, 8, 2, 6, 2, 8, - 1,
-	1, 5, 6, 2, 1, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	1, 3, 6, 1, 6, 10, 3, 8, 6, 5, 6, 9, 8, 9, 6, - 1,
-	10, 1, 0, 10, 0, 6, 9, 5, 0, 5, 6, 0, - 1, - 1, - 1, - 1,
-	0, 3, 8, 5, 6, 10, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	10, 5, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	11, 5, 10, 7, 5, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	11, 5, 10, 11, 7, 5, 8, 3, 0, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	5, 11, 7, 5, 10, 11, 1, 9, 0, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	10, 7, 5, 10, 11, 7, 9, 8, 1, 8, 3, 1, - 1, - 1, - 1, - 1,
-	11, 1, 2, 11, 7, 1, 7, 5, 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	0, 8, 3, 1, 2, 7, 1, 7, 5, 7, 2, 11, - 1, - 1, - 1, - 1,
-	9, 7, 5, 9, 2, 7, 9, 0, 2, 2, 11, 7, - 1, - 1, - 1, - 1,
-	7, 5, 2, 7, 2, 11, 5, 9, 2, 3, 2, 8, 9, 8, 2, - 1,
-	2, 5, 10, 2, 3, 5, 3, 7, 5, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	8, 2, 0, 8, 5, 2, 8, 7, 5, 10, 2, 5, - 1, - 1, - 1, - 1,
-	9, 0, 1, 5, 10, 3, 5, 3, 7, 3, 10, 2, - 1, - 1, - 1, - 1,
-	9, 8, 2, 9, 2, 1, 8, 7, 2, 10, 2, 5, 7, 5, 2, - 1,
-	1, 3, 5, 3, 7, 5, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	0, 8, 7, 0, 7, 1, 1, 7, 5, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	9, 0, 3, 9, 3, 5, 5, 3, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	9, 8, 7, 5, 9, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	5, 8, 4, 5, 10, 8, 10, 11, 8, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	5, 0, 4, 5, 11, 0, 5, 10, 11, 11, 3, 0, - 1, - 1, - 1, - 1,
-	0, 1, 9, 8, 4, 10, 8, 10, 11, 10, 4, 5, - 1, - 1, - 1, - 1,
-	10, 11, 4, 10, 4, 5, 11, 3, 4, 9, 4, 1, 3, 1, 4, - 1,
-	2, 5, 1, 2, 8, 5, 2, 11, 8, 4, 5, 8, - 1, - 1, - 1, - 1,
-	0, 4, 11, 0, 11, 3, 4, 5, 11, 2, 11, 1, 5, 1, 11, - 1,
-	0, 2, 5, 0, 5, 9, 2, 11, 5, 4, 5, 8, 11, 8, 5, - 1,
-	9, 4, 5, 2, 11, 3, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	2, 5, 10, 3, 5, 2, 3, 4, 5, 3, 8, 4, - 1, - 1, - 1, - 1,
-	5, 10, 2, 5, 2, 4, 4, 2, 0, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	3, 10, 2, 3, 5, 10, 3, 8, 5, 4, 5, 8, 0, 1, 9, - 1,
-	5, 10, 2, 5, 2, 4, 1, 9, 2, 9, 4, 2, - 1, - 1, - 1, - 1,
-	8, 4, 5, 8, 5, 3, 3, 5, 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	0, 4, 5, 1, 0, 5, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	8, 4, 5, 8, 5, 3, 9, 0, 5, 0, 3, 5, - 1, - 1, - 1, - 1,
-	9, 4, 5, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	4, 11, 7, 4, 9, 11, 9, 10, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	0, 8, 3, 4, 9, 7, 9, 11, 7, 9, 10, 11, - 1, - 1, - 1, - 1,
-	1, 10, 11, 1, 11, 4, 1, 4, 0, 7, 4, 11, - 1, - 1, - 1, - 1,
-	3, 1, 4, 3, 4, 8, 1, 10, 4, 7, 4, 11, 10, 11, 4, - 1,
-	4, 11, 7, 9, 11, 4, 9, 2, 11, 9, 1, 2, - 1, - 1, - 1, - 1,
-	9, 7, 4, 9, 11, 7, 9, 1, 11, 2, 11, 1, 0, 8, 3, - 1,
-	11, 7, 4, 11, 4, 2, 2, 4, 0, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	11, 7, 4, 11, 4, 2, 8, 3, 4, 3, 2, 4, - 1, - 1, - 1, - 1,
-	2, 9, 10, 2, 7, 9, 2, 3, 7, 7, 4, 9, - 1, - 1, - 1, - 1,
-	9, 10, 7, 9, 7, 4, 10, 2, 7, 8, 7, 0, 2, 0, 7, - 1,
-	3, 7, 10, 3, 10, 2, 7, 4, 10, 1, 10, 0, 4, 0, 10, - 1,
-	1, 10, 2, 8, 7, 4, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	4, 9, 1, 4, 1, 7, 7, 1, 3, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	4, 9, 1, 4, 1, 7, 0, 8, 1, 8, 7, 1, - 1, - 1, - 1, - 1,
-	4, 0, 3, 7, 4, 3, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	4, 8, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	9, 10, 8, 10, 11, 8, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	3, 0, 9, 3, 9, 11, 11, 9, 10, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	0, 1, 10, 0, 10, 8, 8, 10, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	3, 1, 10, 11, 3, 10, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	1, 2, 11, 1, 11, 9, 9, 11, 8, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	3, 0, 9, 3, 9, 11, 1, 2, 9, 2, 11, 9, - 1, - 1, - 1, - 1,
-	0, 2, 11, 8, 0, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	3, 2, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	2, 3, 8, 2, 8, 10, 10, 8, 9, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	9, 10, 2, 0, 9, 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	2, 3, 8, 2, 8, 10, 0, 1, 8, 1, 10, 8, - 1, - 1, - 1, - 1,
-	1, 10, 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	1, 3, 8, 9, 1, 8, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	0, 9, 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	0, 3, 8, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-	- 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1 ] );
+	THREE.MarchingCubes.prototype = Object.create( THREE.ImmediateRenderObject.prototype );
+	THREE.MarchingCubes.prototype.constructor = THREE.MarchingCubes;
+
+
+	/////////////////////////////////////
+	// Marching cubes lookup tables
+	/////////////////////////////////////
+
+	// These tables are straight from Paul Bourke's page:
+	// http://local.wasp.uwa.edu.au/~pbourke/geometry/polygonise/
+	// who in turn got them from Cory Gene Bloyd.
+
+	var EdgeTable = new Int32Array( [
+		0x0, 0x109, 0x203, 0x30a, 0x406, 0x50f, 0x605, 0x70c,
+		0x80c, 0x905, 0xa0f, 0xb06, 0xc0a, 0xd03, 0xe09, 0xf00,
+		0x190, 0x99, 0x393, 0x29a, 0x596, 0x49f, 0x795, 0x69c,
+		0x99c, 0x895, 0xb9f, 0xa96, 0xd9a, 0xc93, 0xf99, 0xe90,
+		0x230, 0x339, 0x33, 0x13a, 0x636, 0x73f, 0x435, 0x53c,
+		0xa3c, 0xb35, 0x83f, 0x936, 0xe3a, 0xf33, 0xc39, 0xd30,
+		0x3a0, 0x2a9, 0x1a3, 0xaa, 0x7a6, 0x6af, 0x5a5, 0x4ac,
+		0xbac, 0xaa5, 0x9af, 0x8a6, 0xfaa, 0xea3, 0xda9, 0xca0,
+		0x460, 0x569, 0x663, 0x76a, 0x66, 0x16f, 0x265, 0x36c,
+		0xc6c, 0xd65, 0xe6f, 0xf66, 0x86a, 0x963, 0xa69, 0xb60,
+		0x5f0, 0x4f9, 0x7f3, 0x6fa, 0x1f6, 0xff, 0x3f5, 0x2fc,
+		0xdfc, 0xcf5, 0xfff, 0xef6, 0x9fa, 0x8f3, 0xbf9, 0xaf0,
+		0x650, 0x759, 0x453, 0x55a, 0x256, 0x35f, 0x55, 0x15c,
+		0xe5c, 0xf55, 0xc5f, 0xd56, 0xa5a, 0xb53, 0x859, 0x950,
+		0x7c0, 0x6c9, 0x5c3, 0x4ca, 0x3c6, 0x2cf, 0x1c5, 0xcc,
+		0xfcc, 0xec5, 0xdcf, 0xcc6, 0xbca, 0xac3, 0x9c9, 0x8c0,
+		0x8c0, 0x9c9, 0xac3, 0xbca, 0xcc6, 0xdcf, 0xec5, 0xfcc,
+		0xcc, 0x1c5, 0x2cf, 0x3c6, 0x4ca, 0x5c3, 0x6c9, 0x7c0,
+		0x950, 0x859, 0xb53, 0xa5a, 0xd56, 0xc5f, 0xf55, 0xe5c,
+		0x15c, 0x55, 0x35f, 0x256, 0x55a, 0x453, 0x759, 0x650,
+		0xaf0, 0xbf9, 0x8f3, 0x9fa, 0xef6, 0xfff, 0xcf5, 0xdfc,
+		0x2fc, 0x3f5, 0xff, 0x1f6, 0x6fa, 0x7f3, 0x4f9, 0x5f0,
+		0xb60, 0xa69, 0x963, 0x86a, 0xf66, 0xe6f, 0xd65, 0xc6c,
+		0x36c, 0x265, 0x16f, 0x66, 0x76a, 0x663, 0x569, 0x460,
+		0xca0, 0xda9, 0xea3, 0xfaa, 0x8a6, 0x9af, 0xaa5, 0xbac,
+		0x4ac, 0x5a5, 0x6af, 0x7a6, 0xaa, 0x1a3, 0x2a9, 0x3a0,
+		0xd30, 0xc39, 0xf33, 0xe3a, 0x936, 0x83f, 0xb35, 0xa3c,
+		0x53c, 0x435, 0x73f, 0x636, 0x13a, 0x33, 0x339, 0x230,
+		0xe90, 0xf99, 0xc93, 0xd9a, 0xa96, 0xb9f, 0x895, 0x99c,
+		0x69c, 0x795, 0x49f, 0x596, 0x29a, 0x393, 0x99, 0x190,
+		0xf00, 0xe09, 0xd03, 0xc0a, 0xb06, 0xa0f, 0x905, 0x80c,
+		0x70c, 0x605, 0x50f, 0x406, 0x30a, 0x203, 0x109, 0x0 ] );
+
+	var TriTable = new Int32Array( [
+		- 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		0, 8, 3, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		0, 1, 9, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		1, 8, 3, 9, 8, 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		1, 2, 10, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		0, 8, 3, 1, 2, 10, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		9, 2, 10, 0, 2, 9, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		2, 8, 3, 2, 10, 8, 10, 9, 8, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		3, 11, 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		0, 11, 2, 8, 11, 0, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		1, 9, 0, 2, 3, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		1, 11, 2, 1, 9, 11, 9, 8, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		3, 10, 1, 11, 10, 3, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		0, 10, 1, 0, 8, 10, 8, 11, 10, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		3, 9, 0, 3, 11, 9, 11, 10, 9, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		9, 8, 10, 10, 8, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		4, 7, 8, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		4, 3, 0, 7, 3, 4, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		0, 1, 9, 8, 4, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		4, 1, 9, 4, 7, 1, 7, 3, 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		1, 2, 10, 8, 4, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		3, 4, 7, 3, 0, 4, 1, 2, 10, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		9, 2, 10, 9, 0, 2, 8, 4, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		2, 10, 9, 2, 9, 7, 2, 7, 3, 7, 9, 4, - 1, - 1, - 1, - 1,
+		8, 4, 7, 3, 11, 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		11, 4, 7, 11, 2, 4, 2, 0, 4, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		9, 0, 1, 8, 4, 7, 2, 3, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		4, 7, 11, 9, 4, 11, 9, 11, 2, 9, 2, 1, - 1, - 1, - 1, - 1,
+		3, 10, 1, 3, 11, 10, 7, 8, 4, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		1, 11, 10, 1, 4, 11, 1, 0, 4, 7, 11, 4, - 1, - 1, - 1, - 1,
+		4, 7, 8, 9, 0, 11, 9, 11, 10, 11, 0, 3, - 1, - 1, - 1, - 1,
+		4, 7, 11, 4, 11, 9, 9, 11, 10, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		9, 5, 4, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		9, 5, 4, 0, 8, 3, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		0, 5, 4, 1, 5, 0, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		8, 5, 4, 8, 3, 5, 3, 1, 5, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		1, 2, 10, 9, 5, 4, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		3, 0, 8, 1, 2, 10, 4, 9, 5, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		5, 2, 10, 5, 4, 2, 4, 0, 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		2, 10, 5, 3, 2, 5, 3, 5, 4, 3, 4, 8, - 1, - 1, - 1, - 1,
+		9, 5, 4, 2, 3, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		0, 11, 2, 0, 8, 11, 4, 9, 5, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		0, 5, 4, 0, 1, 5, 2, 3, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		2, 1, 5, 2, 5, 8, 2, 8, 11, 4, 8, 5, - 1, - 1, - 1, - 1,
+		10, 3, 11, 10, 1, 3, 9, 5, 4, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		4, 9, 5, 0, 8, 1, 8, 10, 1, 8, 11, 10, - 1, - 1, - 1, - 1,
+		5, 4, 0, 5, 0, 11, 5, 11, 10, 11, 0, 3, - 1, - 1, - 1, - 1,
+		5, 4, 8, 5, 8, 10, 10, 8, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		9, 7, 8, 5, 7, 9, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		9, 3, 0, 9, 5, 3, 5, 7, 3, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		0, 7, 8, 0, 1, 7, 1, 5, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		1, 5, 3, 3, 5, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		9, 7, 8, 9, 5, 7, 10, 1, 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		10, 1, 2, 9, 5, 0, 5, 3, 0, 5, 7, 3, - 1, - 1, - 1, - 1,
+		8, 0, 2, 8, 2, 5, 8, 5, 7, 10, 5, 2, - 1, - 1, - 1, - 1,
+		2, 10, 5, 2, 5, 3, 3, 5, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		7, 9, 5, 7, 8, 9, 3, 11, 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		9, 5, 7, 9, 7, 2, 9, 2, 0, 2, 7, 11, - 1, - 1, - 1, - 1,
+		2, 3, 11, 0, 1, 8, 1, 7, 8, 1, 5, 7, - 1, - 1, - 1, - 1,
+		11, 2, 1, 11, 1, 7, 7, 1, 5, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		9, 5, 8, 8, 5, 7, 10, 1, 3, 10, 3, 11, - 1, - 1, - 1, - 1,
+		5, 7, 0, 5, 0, 9, 7, 11, 0, 1, 0, 10, 11, 10, 0, - 1,
+		11, 10, 0, 11, 0, 3, 10, 5, 0, 8, 0, 7, 5, 7, 0, - 1,
+		11, 10, 5, 7, 11, 5, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		10, 6, 5, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		0, 8, 3, 5, 10, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		9, 0, 1, 5, 10, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		1, 8, 3, 1, 9, 8, 5, 10, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		1, 6, 5, 2, 6, 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		1, 6, 5, 1, 2, 6, 3, 0, 8, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		9, 6, 5, 9, 0, 6, 0, 2, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		5, 9, 8, 5, 8, 2, 5, 2, 6, 3, 2, 8, - 1, - 1, - 1, - 1,
+		2, 3, 11, 10, 6, 5, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		11, 0, 8, 11, 2, 0, 10, 6, 5, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		0, 1, 9, 2, 3, 11, 5, 10, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		5, 10, 6, 1, 9, 2, 9, 11, 2, 9, 8, 11, - 1, - 1, - 1, - 1,
+		6, 3, 11, 6, 5, 3, 5, 1, 3, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		0, 8, 11, 0, 11, 5, 0, 5, 1, 5, 11, 6, - 1, - 1, - 1, - 1,
+		3, 11, 6, 0, 3, 6, 0, 6, 5, 0, 5, 9, - 1, - 1, - 1, - 1,
+		6, 5, 9, 6, 9, 11, 11, 9, 8, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		5, 10, 6, 4, 7, 8, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		4, 3, 0, 4, 7, 3, 6, 5, 10, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		1, 9, 0, 5, 10, 6, 8, 4, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		10, 6, 5, 1, 9, 7, 1, 7, 3, 7, 9, 4, - 1, - 1, - 1, - 1,
+		6, 1, 2, 6, 5, 1, 4, 7, 8, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		1, 2, 5, 5, 2, 6, 3, 0, 4, 3, 4, 7, - 1, - 1, - 1, - 1,
+		8, 4, 7, 9, 0, 5, 0, 6, 5, 0, 2, 6, - 1, - 1, - 1, - 1,
+		7, 3, 9, 7, 9, 4, 3, 2, 9, 5, 9, 6, 2, 6, 9, - 1,
+		3, 11, 2, 7, 8, 4, 10, 6, 5, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		5, 10, 6, 4, 7, 2, 4, 2, 0, 2, 7, 11, - 1, - 1, - 1, - 1,
+		0, 1, 9, 4, 7, 8, 2, 3, 11, 5, 10, 6, - 1, - 1, - 1, - 1,
+		9, 2, 1, 9, 11, 2, 9, 4, 11, 7, 11, 4, 5, 10, 6, - 1,
+		8, 4, 7, 3, 11, 5, 3, 5, 1, 5, 11, 6, - 1, - 1, - 1, - 1,
+		5, 1, 11, 5, 11, 6, 1, 0, 11, 7, 11, 4, 0, 4, 11, - 1,
+		0, 5, 9, 0, 6, 5, 0, 3, 6, 11, 6, 3, 8, 4, 7, - 1,
+		6, 5, 9, 6, 9, 11, 4, 7, 9, 7, 11, 9, - 1, - 1, - 1, - 1,
+		10, 4, 9, 6, 4, 10, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		4, 10, 6, 4, 9, 10, 0, 8, 3, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		10, 0, 1, 10, 6, 0, 6, 4, 0, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		8, 3, 1, 8, 1, 6, 8, 6, 4, 6, 1, 10, - 1, - 1, - 1, - 1,
+		1, 4, 9, 1, 2, 4, 2, 6, 4, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		3, 0, 8, 1, 2, 9, 2, 4, 9, 2, 6, 4, - 1, - 1, - 1, - 1,
+		0, 2, 4, 4, 2, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		8, 3, 2, 8, 2, 4, 4, 2, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		10, 4, 9, 10, 6, 4, 11, 2, 3, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		0, 8, 2, 2, 8, 11, 4, 9, 10, 4, 10, 6, - 1, - 1, - 1, - 1,
+		3, 11, 2, 0, 1, 6, 0, 6, 4, 6, 1, 10, - 1, - 1, - 1, - 1,
+		6, 4, 1, 6, 1, 10, 4, 8, 1, 2, 1, 11, 8, 11, 1, - 1,
+		9, 6, 4, 9, 3, 6, 9, 1, 3, 11, 6, 3, - 1, - 1, - 1, - 1,
+		8, 11, 1, 8, 1, 0, 11, 6, 1, 9, 1, 4, 6, 4, 1, - 1,
+		3, 11, 6, 3, 6, 0, 0, 6, 4, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		6, 4, 8, 11, 6, 8, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		7, 10, 6, 7, 8, 10, 8, 9, 10, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		0, 7, 3, 0, 10, 7, 0, 9, 10, 6, 7, 10, - 1, - 1, - 1, - 1,
+		10, 6, 7, 1, 10, 7, 1, 7, 8, 1, 8, 0, - 1, - 1, - 1, - 1,
+		10, 6, 7, 10, 7, 1, 1, 7, 3, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		1, 2, 6, 1, 6, 8, 1, 8, 9, 8, 6, 7, - 1, - 1, - 1, - 1,
+		2, 6, 9, 2, 9, 1, 6, 7, 9, 0, 9, 3, 7, 3, 9, - 1,
+		7, 8, 0, 7, 0, 6, 6, 0, 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		7, 3, 2, 6, 7, 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		2, 3, 11, 10, 6, 8, 10, 8, 9, 8, 6, 7, - 1, - 1, - 1, - 1,
+		2, 0, 7, 2, 7, 11, 0, 9, 7, 6, 7, 10, 9, 10, 7, - 1,
+		1, 8, 0, 1, 7, 8, 1, 10, 7, 6, 7, 10, 2, 3, 11, - 1,
+		11, 2, 1, 11, 1, 7, 10, 6, 1, 6, 7, 1, - 1, - 1, - 1, - 1,
+		8, 9, 6, 8, 6, 7, 9, 1, 6, 11, 6, 3, 1, 3, 6, - 1,
+		0, 9, 1, 11, 6, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		7, 8, 0, 7, 0, 6, 3, 11, 0, 11, 6, 0, - 1, - 1, - 1, - 1,
+		7, 11, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		7, 6, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		3, 0, 8, 11, 7, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		0, 1, 9, 11, 7, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		8, 1, 9, 8, 3, 1, 11, 7, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		10, 1, 2, 6, 11, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		1, 2, 10, 3, 0, 8, 6, 11, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		2, 9, 0, 2, 10, 9, 6, 11, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		6, 11, 7, 2, 10, 3, 10, 8, 3, 10, 9, 8, - 1, - 1, - 1, - 1,
+		7, 2, 3, 6, 2, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		7, 0, 8, 7, 6, 0, 6, 2, 0, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		2, 7, 6, 2, 3, 7, 0, 1, 9, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		1, 6, 2, 1, 8, 6, 1, 9, 8, 8, 7, 6, - 1, - 1, - 1, - 1,
+		10, 7, 6, 10, 1, 7, 1, 3, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		10, 7, 6, 1, 7, 10, 1, 8, 7, 1, 0, 8, - 1, - 1, - 1, - 1,
+		0, 3, 7, 0, 7, 10, 0, 10, 9, 6, 10, 7, - 1, - 1, - 1, - 1,
+		7, 6, 10, 7, 10, 8, 8, 10, 9, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		6, 8, 4, 11, 8, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		3, 6, 11, 3, 0, 6, 0, 4, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		8, 6, 11, 8, 4, 6, 9, 0, 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		9, 4, 6, 9, 6, 3, 9, 3, 1, 11, 3, 6, - 1, - 1, - 1, - 1,
+		6, 8, 4, 6, 11, 8, 2, 10, 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		1, 2, 10, 3, 0, 11, 0, 6, 11, 0, 4, 6, - 1, - 1, - 1, - 1,
+		4, 11, 8, 4, 6, 11, 0, 2, 9, 2, 10, 9, - 1, - 1, - 1, - 1,
+		10, 9, 3, 10, 3, 2, 9, 4, 3, 11, 3, 6, 4, 6, 3, - 1,
+		8, 2, 3, 8, 4, 2, 4, 6, 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		0, 4, 2, 4, 6, 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		1, 9, 0, 2, 3, 4, 2, 4, 6, 4, 3, 8, - 1, - 1, - 1, - 1,
+		1, 9, 4, 1, 4, 2, 2, 4, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		8, 1, 3, 8, 6, 1, 8, 4, 6, 6, 10, 1, - 1, - 1, - 1, - 1,
+		10, 1, 0, 10, 0, 6, 6, 0, 4, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		4, 6, 3, 4, 3, 8, 6, 10, 3, 0, 3, 9, 10, 9, 3, - 1,
+		10, 9, 4, 6, 10, 4, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		4, 9, 5, 7, 6, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		0, 8, 3, 4, 9, 5, 11, 7, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		5, 0, 1, 5, 4, 0, 7, 6, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		11, 7, 6, 8, 3, 4, 3, 5, 4, 3, 1, 5, - 1, - 1, - 1, - 1,
+		9, 5, 4, 10, 1, 2, 7, 6, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		6, 11, 7, 1, 2, 10, 0, 8, 3, 4, 9, 5, - 1, - 1, - 1, - 1,
+		7, 6, 11, 5, 4, 10, 4, 2, 10, 4, 0, 2, - 1, - 1, - 1, - 1,
+		3, 4, 8, 3, 5, 4, 3, 2, 5, 10, 5, 2, 11, 7, 6, - 1,
+		7, 2, 3, 7, 6, 2, 5, 4, 9, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		9, 5, 4, 0, 8, 6, 0, 6, 2, 6, 8, 7, - 1, - 1, - 1, - 1,
+		3, 6, 2, 3, 7, 6, 1, 5, 0, 5, 4, 0, - 1, - 1, - 1, - 1,
+		6, 2, 8, 6, 8, 7, 2, 1, 8, 4, 8, 5, 1, 5, 8, - 1,
+		9, 5, 4, 10, 1, 6, 1, 7, 6, 1, 3, 7, - 1, - 1, - 1, - 1,
+		1, 6, 10, 1, 7, 6, 1, 0, 7, 8, 7, 0, 9, 5, 4, - 1,
+		4, 0, 10, 4, 10, 5, 0, 3, 10, 6, 10, 7, 3, 7, 10, - 1,
+		7, 6, 10, 7, 10, 8, 5, 4, 10, 4, 8, 10, - 1, - 1, - 1, - 1,
+		6, 9, 5, 6, 11, 9, 11, 8, 9, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		3, 6, 11, 0, 6, 3, 0, 5, 6, 0, 9, 5, - 1, - 1, - 1, - 1,
+		0, 11, 8, 0, 5, 11, 0, 1, 5, 5, 6, 11, - 1, - 1, - 1, - 1,
+		6, 11, 3, 6, 3, 5, 5, 3, 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		1, 2, 10, 9, 5, 11, 9, 11, 8, 11, 5, 6, - 1, - 1, - 1, - 1,
+		0, 11, 3, 0, 6, 11, 0, 9, 6, 5, 6, 9, 1, 2, 10, - 1,
+		11, 8, 5, 11, 5, 6, 8, 0, 5, 10, 5, 2, 0, 2, 5, - 1,
+		6, 11, 3, 6, 3, 5, 2, 10, 3, 10, 5, 3, - 1, - 1, - 1, - 1,
+		5, 8, 9, 5, 2, 8, 5, 6, 2, 3, 8, 2, - 1, - 1, - 1, - 1,
+		9, 5, 6, 9, 6, 0, 0, 6, 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		1, 5, 8, 1, 8, 0, 5, 6, 8, 3, 8, 2, 6, 2, 8, - 1,
+		1, 5, 6, 2, 1, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		1, 3, 6, 1, 6, 10, 3, 8, 6, 5, 6, 9, 8, 9, 6, - 1,
+		10, 1, 0, 10, 0, 6, 9, 5, 0, 5, 6, 0, - 1, - 1, - 1, - 1,
+		0, 3, 8, 5, 6, 10, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		10, 5, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		11, 5, 10, 7, 5, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		11, 5, 10, 11, 7, 5, 8, 3, 0, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		5, 11, 7, 5, 10, 11, 1, 9, 0, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		10, 7, 5, 10, 11, 7, 9, 8, 1, 8, 3, 1, - 1, - 1, - 1, - 1,
+		11, 1, 2, 11, 7, 1, 7, 5, 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		0, 8, 3, 1, 2, 7, 1, 7, 5, 7, 2, 11, - 1, - 1, - 1, - 1,
+		9, 7, 5, 9, 2, 7, 9, 0, 2, 2, 11, 7, - 1, - 1, - 1, - 1,
+		7, 5, 2, 7, 2, 11, 5, 9, 2, 3, 2, 8, 9, 8, 2, - 1,
+		2, 5, 10, 2, 3, 5, 3, 7, 5, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		8, 2, 0, 8, 5, 2, 8, 7, 5, 10, 2, 5, - 1, - 1, - 1, - 1,
+		9, 0, 1, 5, 10, 3, 5, 3, 7, 3, 10, 2, - 1, - 1, - 1, - 1,
+		9, 8, 2, 9, 2, 1, 8, 7, 2, 10, 2, 5, 7, 5, 2, - 1,
+		1, 3, 5, 3, 7, 5, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		0, 8, 7, 0, 7, 1, 1, 7, 5, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		9, 0, 3, 9, 3, 5, 5, 3, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		9, 8, 7, 5, 9, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		5, 8, 4, 5, 10, 8, 10, 11, 8, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		5, 0, 4, 5, 11, 0, 5, 10, 11, 11, 3, 0, - 1, - 1, - 1, - 1,
+		0, 1, 9, 8, 4, 10, 8, 10, 11, 10, 4, 5, - 1, - 1, - 1, - 1,
+		10, 11, 4, 10, 4, 5, 11, 3, 4, 9, 4, 1, 3, 1, 4, - 1,
+		2, 5, 1, 2, 8, 5, 2, 11, 8, 4, 5, 8, - 1, - 1, - 1, - 1,
+		0, 4, 11, 0, 11, 3, 4, 5, 11, 2, 11, 1, 5, 1, 11, - 1,
+		0, 2, 5, 0, 5, 9, 2, 11, 5, 4, 5, 8, 11, 8, 5, - 1,
+		9, 4, 5, 2, 11, 3, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		2, 5, 10, 3, 5, 2, 3, 4, 5, 3, 8, 4, - 1, - 1, - 1, - 1,
+		5, 10, 2, 5, 2, 4, 4, 2, 0, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		3, 10, 2, 3, 5, 10, 3, 8, 5, 4, 5, 8, 0, 1, 9, - 1,
+		5, 10, 2, 5, 2, 4, 1, 9, 2, 9, 4, 2, - 1, - 1, - 1, - 1,
+		8, 4, 5, 8, 5, 3, 3, 5, 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		0, 4, 5, 1, 0, 5, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		8, 4, 5, 8, 5, 3, 9, 0, 5, 0, 3, 5, - 1, - 1, - 1, - 1,
+		9, 4, 5, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		4, 11, 7, 4, 9, 11, 9, 10, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		0, 8, 3, 4, 9, 7, 9, 11, 7, 9, 10, 11, - 1, - 1, - 1, - 1,
+		1, 10, 11, 1, 11, 4, 1, 4, 0, 7, 4, 11, - 1, - 1, - 1, - 1,
+		3, 1, 4, 3, 4, 8, 1, 10, 4, 7, 4, 11, 10, 11, 4, - 1,
+		4, 11, 7, 9, 11, 4, 9, 2, 11, 9, 1, 2, - 1, - 1, - 1, - 1,
+		9, 7, 4, 9, 11, 7, 9, 1, 11, 2, 11, 1, 0, 8, 3, - 1,
+		11, 7, 4, 11, 4, 2, 2, 4, 0, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		11, 7, 4, 11, 4, 2, 8, 3, 4, 3, 2, 4, - 1, - 1, - 1, - 1,
+		2, 9, 10, 2, 7, 9, 2, 3, 7, 7, 4, 9, - 1, - 1, - 1, - 1,
+		9, 10, 7, 9, 7, 4, 10, 2, 7, 8, 7, 0, 2, 0, 7, - 1,
+		3, 7, 10, 3, 10, 2, 7, 4, 10, 1, 10, 0, 4, 0, 10, - 1,
+		1, 10, 2, 8, 7, 4, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		4, 9, 1, 4, 1, 7, 7, 1, 3, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		4, 9, 1, 4, 1, 7, 0, 8, 1, 8, 7, 1, - 1, - 1, - 1, - 1,
+		4, 0, 3, 7, 4, 3, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		4, 8, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		9, 10, 8, 10, 11, 8, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		3, 0, 9, 3, 9, 11, 11, 9, 10, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		0, 1, 10, 0, 10, 8, 8, 10, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		3, 1, 10, 11, 3, 10, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		1, 2, 11, 1, 11, 9, 9, 11, 8, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		3, 0, 9, 3, 9, 11, 1, 2, 9, 2, 11, 9, - 1, - 1, - 1, - 1,
+		0, 2, 11, 8, 0, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		3, 2, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		2, 3, 8, 2, 8, 10, 10, 8, 9, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		9, 10, 2, 0, 9, 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		2, 3, 8, 2, 8, 10, 0, 1, 8, 1, 10, 8, - 1, - 1, - 1, - 1,
+		1, 10, 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		1, 3, 8, 9, 1, 8, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		0, 9, 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		0, 3, 8, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+		- 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1 ] );
+
+} )( THREE )

+ 1 - 1
examples/js/controls/DragControls.js

@@ -154,7 +154,7 @@ THREE.DragControls = function ( _objects, _camera, _domElement ) {
 
 		}
 
-		_domElement.style.cursor = 'auto';
+		_domElement.style.cursor = _hovered ? 'pointer' : 'auto';
 
 	}
 

+ 10 - 8
examples/js/controls/EditorControls.js

@@ -21,6 +21,8 @@ THREE.EditorControls = function ( object, domElement ) {
 
 	var scope = this;
 	var vector = new THREE.Vector3();
+	var delta = new THREE.Vector3();
+	var box = new THREE.Box3();
 
 	var STATE = { NONE: - 1, ROTATE: 0, ZOOM: 1, PAN: 2 };
 	var state = STATE.NONE;
@@ -37,10 +39,10 @@ THREE.EditorControls = function ( object, domElement ) {
 
 	this.focus = function ( target ) {
 
-		var box = new THREE.Box3().setFromObject( target );
-
 		var distance;
 
+		box.setFromObject( target );
+
 		if ( box.isEmpty() === false ) {
 
 			center.copy( box.getCenter() );
@@ -55,7 +57,7 @@ THREE.EditorControls = function ( object, domElement ) {
 
 		}
 
-		var delta = new THREE.Vector3( 0, 0, 1 );
+		delta.set( 0, 0, 1 );
 		delta.applyQuaternion( object.quaternion );
 		delta.multiplyScalar( distance * 4 );
 
@@ -156,15 +158,15 @@ THREE.EditorControls = function ( object, domElement ) {
 
 		if ( state === STATE.ROTATE ) {
 
-			scope.rotate( new THREE.Vector3( - movementX * scope.rotationSpeed, - movementY * scope.rotationSpeed, 0 ) );
+			scope.rotate( delta.set( - movementX * scope.rotationSpeed, - movementY * scope.rotationSpeed, 0 ) );
 
 		} else if ( state === STATE.ZOOM ) {
 
-			scope.zoom( new THREE.Vector3( 0, 0, movementY ) );
+			scope.zoom( delta.set( 0, 0, movementY ) );
 
 		} else if ( state === STATE.PAN ) {
 
-			scope.pan( new THREE.Vector3( - movementX, movementY, 0 ) );
+			scope.pan( delta.set( - movementX, movementY, 0 ) );
 
 		}
 
@@ -188,7 +190,7 @@ THREE.EditorControls = function ( object, domElement ) {
 		event.preventDefault();
 
 		// Normalize deltaY due to https://bugzilla.mozilla.org/show_bug.cgi?id=1392460
-		scope.zoom( new THREE.Vector3( 0, 0, event.deltaY > 0 ? 1 : - 1 ) );
+		scope.zoom( delta.set( 0, 0, event.deltaY > 0 ? 1 : - 1 ) );
 
 	}
 
@@ -283,7 +285,7 @@ THREE.EditorControls = function ( object, domElement ) {
 				touches[ 0 ].set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY, 0 );
 				touches[ 1 ].set( event.touches[ 1 ].pageX, event.touches[ 1 ].pageY, 0 );
 				var distance = touches[ 0 ].distanceTo( touches[ 1 ] );
-				scope.zoom( new THREE.Vector3( 0, 0, prevDistance - distance ) );
+				scope.zoom( delta.set( 0, 0, prevDistance - distance ) );
 				prevDistance = distance;
 
 

+ 23 - 12
examples/js/controls/MapControls.js

@@ -11,11 +11,10 @@
 // Unlike TrackballControls, it maintains the "up" direction object.up (+Y by default).
 // This is very similar to OrbitControls, another set of touch behavior
 //
-//    Orbit - right mouse / touch: two-finger rotate
+//    Orbit - right mouse, or left mouse + ctrl/metaKey / touch: two-finger rotate
 //    Zoom - middle mouse, or mousewheel / touch: two-finger spread or squish
 //    Pan - left mouse, or arrow keys / touch: one-finger move
 
-
 THREE.MapControls = function ( object, domElement ) {
 
 	this.object = object;
@@ -78,7 +77,7 @@ THREE.MapControls = function ( object, domElement ) {
 	this.keys = { LEFT: 37, UP: 38, RIGHT: 39, BOTTOM: 40 };
 
 	// Mouse buttons
-	this.mouseButtons = { ORBIT: THREE.MOUSE.RIGHT, ZOOM: THREE.MOUSE.MIDDLE, PAN: THREE.MOUSE.LEFT };
+	this.mouseButtons = { LEFT: THREE.MOUSE.LEFT, MIDDLE: THREE.MOUSE.MIDDLE, RIGHT: THREE.MOUSE.RIGHT };
 
 	// for reset
 	this.target0 = this.target.clone();
@@ -776,17 +775,29 @@ THREE.MapControls = function ( object, domElement ) {
 
 		switch ( event.button ) {
 
-			case scope.mouseButtons.ORBIT:
+			case scope.mouseButtons.LEFT:
 
-				if ( scope.enableRotate === false ) return;
+				if ( event.ctrlKey || event.metaKey ) {
 
-				handleMouseDownRotate( event );
+					if ( scope.enableRotate === false ) return;
 
-				state = STATE.ROTATE;
+					handleMouseDownRotate( event );
+
+					state = STATE.ROTATE;
+
+				} else {
+
+					if ( scope.enablePan === false ) return;
+
+					handleMouseDownPan( event );
+
+					state = STATE.PAN;
+
+				}
 
 				break;
 
-			case scope.mouseButtons.ZOOM:
+			case scope.mouseButtons.MIDDLE:
 
 				if ( scope.enableZoom === false ) return;
 
@@ -796,13 +807,13 @@ THREE.MapControls = function ( object, domElement ) {
 
 				break;
 
-			case scope.mouseButtons.PAN:
+			case scope.mouseButtons.RIGHT:
 
-				if ( scope.enablePan === false ) return;
+				if ( scope.enableRotate === false ) return;
 
-				handleMouseDownPan( event );
+				handleMouseDownRotate( event );
 
-				state = STATE.PAN;
+				state = STATE.ROTATE;
 
 				break;
 

+ 20 - 8
examples/js/controls/OrbitControls.js

@@ -11,7 +11,7 @@
 //
 //    Orbit - left mouse / touch: one-finger move
 //    Zoom - middle mouse, or mousewheel / touch: two-finger spread or squish
-//    Pan - right mouse, or arrow keys / touch: two-finger move
+//    Pan - right mouse, or left mouse + ctrl/metaKey, or arrow keys / touch: two-finger move
 
 THREE.OrbitControls = function ( object, domElement ) {
 
@@ -75,7 +75,7 @@ THREE.OrbitControls = function ( object, domElement ) {
 	this.keys = { LEFT: 37, UP: 38, RIGHT: 39, BOTTOM: 40 };
 
 	// Mouse buttons
-	this.mouseButtons = { ORBIT: THREE.MOUSE.LEFT, ZOOM: THREE.MOUSE.MIDDLE, PAN: THREE.MOUSE.RIGHT };
+	this.mouseButtons = { LEFT: THREE.MOUSE.LEFT, MIDDLE: THREE.MOUSE.MIDDLE, RIGHT: THREE.MOUSE.RIGHT };
 
 	// for reset
 	this.target0 = this.target.clone();
@@ -677,17 +677,29 @@ THREE.OrbitControls = function ( object, domElement ) {
 
 		switch ( event.button ) {
 
-			case scope.mouseButtons.ORBIT:
+			case scope.mouseButtons.LEFT:
 
-				if ( scope.enableRotate === false ) return;
+				if ( event.ctrlKey || event.metaKey ) {
+
+					if ( scope.enablePan === false ) return;
+
+					handleMouseDownPan( event );
+
+					state = STATE.PAN;
+
+				} else {
+
+					if ( scope.enableRotate === false ) return;
+
+					handleMouseDownRotate( event );
 
-				handleMouseDownRotate( event );
+					state = STATE.ROTATE;
 
-				state = STATE.ROTATE;
+				}
 
 				break;
 
-			case scope.mouseButtons.ZOOM:
+			case scope.mouseButtons.MIDDLE:
 
 				if ( scope.enableZoom === false ) return;
 
@@ -697,7 +709,7 @@ THREE.OrbitControls = function ( object, domElement ) {
 
 				break;
 
-			case scope.mouseButtons.PAN:
+			case scope.mouseButtons.RIGHT:
 
 				if ( scope.enablePan === false ) return;
 

+ 1019 - 710
examples/js/controls/TransformControls.js

@@ -2,1146 +2,1455 @@
  * @author arodic / https://github.com/arodic
  */
 
-( function () {
+THREE.TransformControls = function ( camera, domElement ) {
 
-	'use strict';
+	THREE.Object3D.call( this );
 
-	var GizmoMaterial = function ( parameters ) {
+	domElement = ( domElement !== undefined ) ? domElement : document;
 
-		THREE.MeshBasicMaterial.call( this );
+	this.visible = false;
 
-		this.depthTest = false;
-		this.depthWrite = false;
-		this.fog = false;
-		this.side = THREE.FrontSide;
-		this.transparent = true;
+	var _gizmo = new THREE.TransformControlsGizmo();
+	this.add( _gizmo );
 
-		this.setValues( parameters );
+	var _plane = new THREE.TransformControlsPlane();
+	this.add( _plane );
 
-		this.oldColor = this.color.clone();
-		this.oldOpacity = this.opacity;
+	var scope = this;
 
-		this.highlight = function ( highlighted ) {
+	// Define properties with getters/setter
+	// Setting the defined property will automatically trigger change event
+	// Defined properties are passed down to gizmo and plane
 
-			if ( highlighted ) {
+	defineProperty( "camera", camera );
+	defineProperty( "object", undefined );
+	defineProperty( "axis", null );
+	defineProperty( "mode", "translate" );
+	defineProperty( "translationSnap", null );
+	defineProperty( "rotationSnap", null );
+	defineProperty( "space", "world" );
+	defineProperty( "size", 1 );
+	defineProperty( "dragging", false );
 
-				this.color.setRGB( 1, 1, 0 );
-				this.opacity = 1;
+	var changeEvent = { type: "change" };
+	var mouseDownEvent = { type: "mouseDown" };
+	var mouseUpEvent = { type: "mouseUp", mode: scope.mode };
+	var objectChangeEvent = { type: "objectChange" };
 
-			} else {
+	// Reusable utility variables
 
-				this.color.copy( this.oldColor );
-				this.opacity = this.oldOpacity;
+	var ray = new THREE.Raycaster();
 
-			}
+	var _tempVector = new THREE.Vector3();
+	var _tempVector2 = new THREE.Vector3();
+	var _tempQuaternion = new THREE.Quaternion();
+	var _unit = {
+		X: new THREE.Vector3( 1, 0, 0 ),
+		Y: new THREE.Vector3( 0, 1, 0 ),
+		Z: new THREE.Vector3( 0, 0, 1 )
+	}
+	var _identityQuaternion = new THREE.Quaternion();
+	var _alignVector = new THREE.Vector3();
 
-		};
+	var pointStart = new THREE.Vector3();
+	var pointEnd = new THREE.Vector3();
+	var rotationAxis = new THREE.Vector3();
+	var rotationAngle = 0;
 
-	};
+	var cameraPosition = new THREE.Vector3();
+	var cameraQuaternion = new THREE.Quaternion();
+	var cameraScale = new THREE.Vector3();
 
-	GizmoMaterial.prototype = Object.create( THREE.MeshBasicMaterial.prototype );
-	GizmoMaterial.prototype.constructor = GizmoMaterial;
+	var parentPosition = new THREE.Vector3();
+	var parentQuaternion = new THREE.Quaternion();
+	var parentScale = new THREE.Vector3();
 
+	var worldPositionStart = new THREE.Vector3();
+	var worldQuaternionStart = new THREE.Quaternion();
+	var worldScaleStart = new THREE.Vector3();
 
-	var GizmoLineMaterial = function ( parameters ) {
+	var worldPosition = new THREE.Vector3();
+	var worldQuaternion = new THREE.Quaternion();
+	var worldScale = new THREE.Vector3();
 
-		THREE.LineBasicMaterial.call( this );
+	var eye = new THREE.Vector3();
 
-		this.depthTest = false;
-		this.depthWrite = false;
-		this.fog = false;
-		this.transparent = true;
-		this.linewidth = 1;
+	var _positionStart = new THREE.Vector3();
+	var _quaternionStart = new THREE.Quaternion();
+	var _scaleStart = new THREE.Vector3();
 
-		this.setValues( parameters );
+	// TODO: remove properties unused in plane and gizmo
 
-		this.oldColor = this.color.clone();
-		this.oldOpacity = this.opacity;
+	defineProperty( "parentQuaternion", parentQuaternion );
+	defineProperty( "worldPosition", worldPosition );
+	defineProperty( "worldPositionStart", worldPositionStart );
+	defineProperty( "worldQuaternion", worldQuaternion );
+	defineProperty( "worldQuaternionStart", worldQuaternionStart );
+	defineProperty( "cameraPosition", cameraPosition );
+	defineProperty( "cameraQuaternion", cameraQuaternion );
+	defineProperty( "pointStart", pointStart );
+	defineProperty( "pointEnd", pointEnd );
+	defineProperty( "rotationAxis", rotationAxis );
+	defineProperty( "rotationAngle", rotationAngle );
+	defineProperty( "eye", eye );
 
-		this.highlight = function ( highlighted ) {
+	{
 
-			if ( highlighted ) {
-
-				this.color.setRGB( 1, 1, 0 );
-				this.opacity = 1;
-
-			} else {
+		domElement.addEventListener( "mousedown", onPointerDown, false );
+		domElement.addEventListener( "touchstart", onPointerDown, false );
+		domElement.addEventListener( "mousemove", onPointerHover, false );
+		domElement.addEventListener( "touchmove", onPointerHover, false );
+		domElement.addEventListener( "mousemove", onPointerMove, false );
+		domElement.addEventListener( "touchmove", onPointerMove, false );
+		domElement.addEventListener( "mouseup", onPointerUp, false );
+		domElement.addEventListener( "mouseleave", onPointerUp, false );
+		domElement.addEventListener( "mouseout", onPointerUp, false );
+		domElement.addEventListener( "touchend", onPointerUp, false );
+		domElement.addEventListener( "touchcancel", onPointerUp, false );
+		domElement.addEventListener( "touchleave", onPointerUp, false );
+		domElement.addEventListener( "contextmenu", onContext, false );
+
+	}
+
+	this.dispose = function () {
+
+		domElement.removeEventListener( "mousedown", onPointerDown );
+		domElement.removeEventListener( "touchstart", onPointerDown );
+		domElement.removeEventListener( "mousemove", onPointerHover );
+		domElement.removeEventListener( "touchmove", onPointerHover );
+		domElement.removeEventListener( "mousemove", onPointerMove );
+		domElement.removeEventListener( "touchmove", onPointerMove );
+		domElement.removeEventListener( "mouseup", onPointerUp );
+		domElement.removeEventListener( "mouseleave", onPointerUp );
+		domElement.removeEventListener( "mouseout", onPointerUp );
+		domElement.removeEventListener( "touchend", onPointerUp );
+		domElement.removeEventListener( "touchcancel", onPointerUp );
+		domElement.removeEventListener( "touchleave", onPointerUp );
+		domElement.removeEventListener( "contextmenu", onContext );
 
-				this.color.copy( this.oldColor );
-				this.opacity = this.oldOpacity;
+	};
 
-			}
+	// Set current object
+	this.attach = function ( object ) {
 
-		};
+		this.object = object;
+		this.visible = true;
 
 	};
 
-	GizmoLineMaterial.prototype = Object.create( THREE.LineBasicMaterial.prototype );
-	GizmoLineMaterial.prototype.constructor = GizmoLineMaterial;
-
-
-	var pickerMaterial = new GizmoMaterial( { visible: false, transparent: false } );
+	// Detatch from object
+	this.detach = function () {
 
+		this.object = undefined;
+		this.visible = false;
+		this.axis = null;
 
-	THREE.TransformGizmo = function () {
+	};
 
-		this.init = function () {
+	// Defined getter, setter and store for a property
+	function defineProperty( propName, defaultValue ) {
 
-			THREE.Object3D.call( this );
+		var propValue = defaultValue;
 
-			this.handles = new THREE.Object3D();
-			this.pickers = new THREE.Object3D();
-			this.planes = new THREE.Object3D();
+		Object.defineProperty( scope, propName, {
 
-			this.add( this.handles );
-			this.add( this.pickers );
-			this.add( this.planes );
+			get: function() {
 
-			//// PLANES
+				return propValue !== undefined ? propValue : defaultValue;
 
-			var planeGeometry = new THREE.PlaneBufferGeometry( 50, 50, 2, 2 );
-			var planeMaterial = new THREE.MeshBasicMaterial( { visible: false, side: THREE.DoubleSide } );
+			},
 
-			var planes = {
-				"XY": new THREE.Mesh( planeGeometry, planeMaterial ),
-				"YZ": new THREE.Mesh( planeGeometry, planeMaterial ),
-				"XZ": new THREE.Mesh( planeGeometry, planeMaterial ),
-				"XYZE": new THREE.Mesh( planeGeometry, planeMaterial )
-			};
+			set: function( value ) {
 
-			this.activePlane = planes[ "XYZE" ];
+				if ( propValue !== value ) {
 
-			planes[ "YZ" ].rotation.set( 0, Math.PI / 2, 0 );
-			planes[ "XZ" ].rotation.set( - Math.PI / 2, 0, 0 );
+					propValue = value;
+					_plane[ propName ] = value;
+					_gizmo[ propName ] = value;
 
-			for ( var i in planes ) {
+					scope.dispatchEvent( changeEvent );
 
-				planes[ i ].name = i;
-				this.planes.add( planes[ i ] );
-				this.planes[ i ] = planes[ i ];
+				}
 
 			}
 
-			//// HANDLES AND PICKERS
-
-			var setupGizmos = function ( gizmoMap, parent ) {
-
-				for ( var name in gizmoMap ) {
-
-					for ( i = gizmoMap[ name ].length; i --; ) {
-
-						var object = gizmoMap[ name ][ i ][ 0 ];
-						var position = gizmoMap[ name ][ i ][ 1 ];
-						var rotation = gizmoMap[ name ][ i ][ 2 ];
-
-						object.name = name;
-
-						object.renderOrder = Infinity; // avoid being hidden by other transparent objects
-
-						if ( position ) object.position.set( position[ 0 ], position[ 1 ], position[ 2 ] );
-						if ( rotation ) object.rotation.set( rotation[ 0 ], rotation[ 1 ], rotation[ 2 ] );
-
-						parent.add( object );
-
-					}
-
-				}
+		});
 
-			};
+		scope[ propName ] = defaultValue;
+		_plane[ propName ] = defaultValue;
+		_gizmo[ propName ] = defaultValue;
 
-			setupGizmos( this.handleGizmos, this.handles );
-			setupGizmos( this.pickerGizmos, this.pickers );
+	}
 
-			// reset Transformations
+	// updateMatrixWorld  updates key transformation variables
+	this.updateMatrixWorld = function () {
 
-			this.traverse( function ( child ) {
+		if ( this.object !== undefined ) {
 
-				if ( child instanceof THREE.Mesh ) {
+			this.object.updateMatrixWorld();
+			this.object.parent.matrixWorld.decompose( parentPosition, parentQuaternion, parentScale );
+			this.object.matrixWorld.decompose( worldPosition, worldQuaternion, worldScale );
 
-					child.updateMatrix();
+		}
 
-					var tempGeometry = child.geometry.clone();
-					tempGeometry.applyMatrix( child.matrix );
-					child.geometry = tempGeometry;
+		this.camera.updateMatrixWorld();
+		this.camera.matrixWorld.decompose( cameraPosition, cameraQuaternion, cameraScale );
 
-					child.position.set( 0, 0, 0 );
-					child.rotation.set( 0, 0, 0 );
-					child.scale.set( 1, 1, 1 );
+		if ( this.camera instanceof THREE.PerspectiveCamera ) {
 
-				}
+			eye.copy( cameraPosition ).sub( worldPosition ).normalize();
 
-			} );
+		} else if ( this.camera instanceof THREE.OrthographicCamera ) {
 
-		};
+			eye.copy( cameraPosition ).normalize();
 
-		this.highlight = function ( axis ) {
+		}
 
-			this.traverse( function ( child ) {
+		THREE.Object3D.prototype.updateMatrixWorld.call( this );
 
-				if ( child.material && child.material.highlight ) {
+	};
 
-					if ( child.name === axis ) {
+	this.pointerHover = function( pointer ) {
 
-						child.material.highlight( true );
+		if ( this.object === undefined || this.dragging === true || ( pointer.button !== undefined && pointer.button !== 0 ) ) return;
 
-					} else {
+		ray.setFromCamera( pointer, this.camera );
 
-						child.material.highlight( false );
+		var intersect = ray.intersectObjects( _gizmo.picker[ this.mode ].children, true )[ 0 ] || false;
 
-					}
+		if ( intersect ) {
 
-				}
+			this.axis = intersect.object.name;
 
-			} );
+		} else {
 
-		};
+			this.axis = null;
 
-	};
+		}
 
-	THREE.TransformGizmo.prototype = Object.create( THREE.Object3D.prototype );
-	THREE.TransformGizmo.prototype.constructor = THREE.TransformGizmo;
+	}
 
-	THREE.TransformGizmo.prototype.update = function ( rotation, eye ) {
+	this.pointerDown = function( pointer ) {
 
-		var vec1 = new THREE.Vector3( 0, 0, 0 );
-		var vec2 = new THREE.Vector3( 0, 1, 0 );
-		var lookAtMatrix = new THREE.Matrix4();
+		if ( this.object === undefined || this.dragging === true || ( pointer.button !== undefined && pointer.button !== 0 ) ) return;
 
-		this.traverse( function ( child ) {
+		if ( ( pointer.button === 0 || pointer.button === undefined ) && this.axis !== null ) {
 
-			if ( child.name.search( "E" ) !== - 1 ) {
+			ray.setFromCamera( pointer, this.camera );
 
-				child.quaternion.setFromRotationMatrix( lookAtMatrix.lookAt( eye, vec1, vec2 ) );
+			var planeIntersect = ray.intersectObjects( [ _plane ], true )[ 0 ] || false;
 
-			} else if ( child.name.search( "X" ) !== - 1 || child.name.search( "Y" ) !== - 1 || child.name.search( "Z" ) !== - 1 ) {
+			if ( planeIntersect ) {
 
-				child.quaternion.setFromEuler( rotation );
+				var space = this.space;
 
-			}
+				if ( this.mode === 'scale') {
 
-		} );
+					space = 'local';
 
-	};
+				} else if ( this.axis === 'E' ||  this.axis === 'XYZE' ||  this.axis === 'XYZ' ) {
 
-	THREE.TransformGizmoTranslate = function () {
+					space = 'world';
 
-		THREE.TransformGizmo.call( this );
+				}
 
-		var arrowGeometry = new THREE.ConeBufferGeometry( 0.05, 0.2, 12, 1, false );
-		arrowGeometry.translate( 0, 0.5, 0 );
+				if ( space === 'local' && this.mode === 'rotate' ) {
 
-		var lineXGeometry = new THREE.BufferGeometry();
-		lineXGeometry.addAttribute( 'position', new THREE.Float32BufferAttribute( [ 0, 0, 0, 1, 0, 0 ], 3 ) );
+					var snap = this.rotationSnap;
 
-		var lineYGeometry = new THREE.BufferGeometry();
-		lineYGeometry.addAttribute( 'position', new THREE.Float32BufferAttribute( [ 0, 0, 0, 0, 1, 0 ], 3 ) );
+					if ( this.axis === 'X' && snap ) this.object.rotation.x = Math.round( this.object.rotation.x / snap ) * snap;
+					if ( this.axis === 'Y' && snap ) this.object.rotation.y = Math.round( this.object.rotation.y / snap ) * snap;
+					if ( this.axis === 'Z' && snap ) this.object.rotation.z = Math.round( this.object.rotation.z / snap ) * snap;
 
-		var lineZGeometry = new THREE.BufferGeometry();
-		lineZGeometry.addAttribute( 'position', new THREE.Float32BufferAttribute( [ 0, 0, 0, 0, 0, 1 ], 3 ) );
+				}
 
-		this.handleGizmos = {
+				this.object.updateMatrixWorld();
+				this.object.parent.updateMatrixWorld();
 
-			X: [
-				[ new THREE.Mesh( arrowGeometry, new GizmoMaterial( { color: 0xff0000 } ) ), [ 0.5, 0, 0 ], [ 0, 0, - Math.PI / 2 ] ],
-				[ new THREE.Line( lineXGeometry, new GizmoLineMaterial( { color: 0xff0000 } ) ) ]
-			],
+				_positionStart.copy( this.object.position );
+				_quaternionStart.copy( this.object.quaternion );
+				_scaleStart.copy( this.object.scale );
 
-			Y: [
-				[ new THREE.Mesh( arrowGeometry, new GizmoMaterial( { color: 0x00ff00 } ) ), [ 0, 0.5, 0 ] ],
-				[	new THREE.Line( lineYGeometry, new GizmoLineMaterial( { color: 0x00ff00 } ) ) ]
-			],
+				this.object.matrixWorld.decompose( worldPositionStart, worldQuaternionStart, worldScaleStart );
 
-			Z: [
-				[ new THREE.Mesh( arrowGeometry, new GizmoMaterial( { color: 0x0000ff } ) ), [ 0, 0, 0.5 ], [ Math.PI / 2, 0, 0 ] ],
-				[ new THREE.Line( lineZGeometry, new GizmoLineMaterial( { color: 0x0000ff } ) ) ]
-			],
+				pointStart.copy( planeIntersect.point ).sub( worldPositionStart );
 
-			XYZ: [
-				[ new THREE.Mesh( new THREE.OctahedronGeometry( 0.1, 0 ), new GizmoMaterial( { color: 0xffffff, opacity: 0.25 } ) ), [ 0, 0, 0 ], [ 0, 0, 0 ] ]
-			],
+				if ( space === 'local' ) pointStart.applyQuaternion( worldQuaternionStart.clone().inverse() );
 
-			XY: [
-				[ new THREE.Mesh( new THREE.PlaneBufferGeometry( 0.29, 0.29 ), new GizmoMaterial( { color: 0xffff00, opacity: 0.25 } ) ), [ 0.15, 0.15, 0 ] ]
-			],
+			}
 
-			YZ: [
-				[ new THREE.Mesh( new THREE.PlaneBufferGeometry( 0.29, 0.29 ), new GizmoMaterial( { color: 0x00ffff, opacity: 0.25 } ) ), [ 0, 0.15, 0.15 ], [ 0, Math.PI / 2, 0 ] ]
-			],
+			this.dragging = true;
+			mouseDownEvent.mode = this.mode;
+			this.dispatchEvent( mouseDownEvent );
 
-			XZ: [
-				[ new THREE.Mesh( new THREE.PlaneBufferGeometry( 0.29, 0.29 ), new GizmoMaterial( { color: 0xff00ff, opacity: 0.25 } ) ), [ 0.15, 0, 0.15 ], [ - Math.PI / 2, 0, 0 ] ]
-			]
+		}
 
-		};
+	}
 
-		this.pickerGizmos = {
+	this.pointerMove = function( pointer ) {
 
-			X: [
-				[ new THREE.Mesh( new THREE.CylinderBufferGeometry( 0.2, 0, 1, 4, 1, false ), pickerMaterial ), [ 0.6, 0, 0 ], [ 0, 0, - Math.PI / 2 ] ]
-			],
+		var axis = this.axis;
+		var mode = this.mode;
+		var object = this.object;
+		var space = this.space;
 
-			Y: [
-				[ new THREE.Mesh( new THREE.CylinderBufferGeometry( 0.2, 0, 1, 4, 1, false ), pickerMaterial ), [ 0, 0.6, 0 ] ]
-			],
+		if ( mode === 'scale') {
 
-			Z: [
-				[ new THREE.Mesh( new THREE.CylinderBufferGeometry( 0.2, 0, 1, 4, 1, false ), pickerMaterial ), [ 0, 0, 0.6 ], [ Math.PI / 2, 0, 0 ] ]
-			],
+			space = 'local';
 
-			XYZ: [
-				[ new THREE.Mesh( new THREE.OctahedronGeometry( 0.2, 0 ), pickerMaterial ) ]
-			],
+		} else if ( axis === 'E' ||  axis === 'XYZE' ||  axis === 'XYZ' ) {
 
-			XY: [
-				[ new THREE.Mesh( new THREE.PlaneBufferGeometry( 0.4, 0.4 ), pickerMaterial ), [ 0.2, 0.2, 0 ] ]
-			],
+			space = 'world';
 
-			YZ: [
-				[ new THREE.Mesh( new THREE.PlaneBufferGeometry( 0.4, 0.4 ), pickerMaterial ), [ 0, 0.2, 0.2 ], [ 0, Math.PI / 2, 0 ] ]
-			],
+		}
 
-			XZ: [
-				[ new THREE.Mesh( new THREE.PlaneBufferGeometry( 0.4, 0.4 ), pickerMaterial ), [ 0.2, 0, 0.2 ], [ - Math.PI / 2, 0, 0 ] ]
-			]
+		if ( object === undefined || axis === null || this.dragging === false || ( pointer.button !== undefined && pointer.button !== 0 ) ) return;
 
-		};
+		ray.setFromCamera( pointer, this.camera );
 
-		this.setActivePlane = function ( axis, eye ) {
+		var planeIntersect = ray.intersectObjects( [ _plane ], true )[ 0 ] || false;
 
-			var tempMatrix = new THREE.Matrix4();
-			eye.applyMatrix4( tempMatrix.getInverse( tempMatrix.extractRotation( this.planes[ "XY" ].matrixWorld ) ) );
+		if ( planeIntersect === false ) return;
 
-			if ( axis === "X" ) {
+		pointEnd.copy( planeIntersect.point ).sub( worldPositionStart );
 
-				this.activePlane = this.planes[ "XY" ];
+		if ( space === 'local' ) pointEnd.applyQuaternion( worldQuaternionStart.clone().inverse() );
 
-				if ( Math.abs( eye.y ) > Math.abs( eye.z ) ) this.activePlane = this.planes[ "XZ" ];
+		if ( mode === 'translate' ) {
 
+			if ( axis.search( 'X' ) === -1 ) {
+				pointEnd.x = pointStart.x;
+			}
+			if ( axis.search( 'Y' ) === -1 ) {
+				pointEnd.y = pointStart.y;
+			}
+			if ( axis.search( 'Z' ) === -1 ) {
+				pointEnd.z = pointStart.z;
 			}
 
-			if ( axis === "Y" ) {
-
-				this.activePlane = this.planes[ "XY" ];
-
-				if ( Math.abs( eye.x ) > Math.abs( eye.z ) ) this.activePlane = this.planes[ "YZ" ];
+			// Apply translate
 
+			if ( space === 'local' ) {
+				object.position.copy( pointEnd ).sub( pointStart ).applyQuaternion( _quaternionStart );
+			} else {
+				object.position.copy( pointEnd ).sub( pointStart );
 			}
 
-			if ( axis === "Z" ) {
+			object.position.add( _positionStart );
 
-				this.activePlane = this.planes[ "XZ" ];
+			// Apply translation snap
 
-				if ( Math.abs( eye.x ) > Math.abs( eye.y ) ) this.activePlane = this.planes[ "YZ" ];
+			if ( this.translationSnap ) {
 
-			}
+				if ( space === 'local' ) {
 
-			if ( axis === "XYZ" ) this.activePlane = this.planes[ "XYZE" ];
+					object.position.applyQuaternion(_tempQuaternion.copy( _quaternionStart ).inverse() );
 
-			if ( axis === "XY" ) this.activePlane = this.planes[ "XY" ];
-
-			if ( axis === "YZ" ) this.activePlane = this.planes[ "YZ" ];
+					if ( axis.search( 'X' ) !== -1 ) {
+						object.position.x = Math.round( object.position.x / this.translationSnap ) * this.translationSnap;
+					}
 
-			if ( axis === "XZ" ) this.activePlane = this.planes[ "XZ" ];
+					if ( axis.search( 'Y' ) !== -1 ) {
+						object.position.y = Math.round( object.position.y / this.translationSnap ) * this.translationSnap;
+					}
 
-		};
+					if ( axis.search( 'Z' ) !== -1 ) {
+						object.position.z = Math.round( object.position.z / this.translationSnap ) * this.translationSnap;
+					}
 
-		this.init();
+					object.position.applyQuaternion( _quaternionStart );
 
-	};
+				}
 
-	THREE.TransformGizmoTranslate.prototype = Object.create( THREE.TransformGizmo.prototype );
-	THREE.TransformGizmoTranslate.prototype.constructor = THREE.TransformGizmoTranslate;
+				if ( space === 'world' ) {
 
-	THREE.TransformGizmoRotate = function () {
+					if ( object.parent ) {
+						object.position.add( _tempVector.setFromMatrixPosition( object.parent.matrixWorld ) );
+					}
 
-		THREE.TransformGizmo.call( this );
+					if ( axis.search( 'X' ) !== -1 ) {
+						object.position.x = Math.round( object.position.x / this.translationSnap ) * this.translationSnap;
+					}
 
-		var CircleGeometry = function ( radius, facing, arc ) {
+					if ( axis.search( 'Y' ) !== -1 ) {
+						object.position.y = Math.round( object.position.y / this.translationSnap ) * this.translationSnap;
+					}
 
-			var geometry = new THREE.BufferGeometry();
-			var vertices = [];
-			arc = arc ? arc : 1;
+					if ( axis.search( 'Z' ) !== -1 ) {
+						object.position.z = Math.round( object.position.z / this.translationSnap ) * this.translationSnap;
+					}
 
-			for ( var i = 0; i <= 64 * arc; ++ i ) {
+					if ( object.parent ) {
+						object.position.sub( _tempVector.setFromMatrixPosition( object.parent.matrixWorld ) );
+					}
 
-				if ( facing === 'x' ) vertices.push( 0, Math.cos( i / 32 * Math.PI ) * radius, Math.sin( i / 32 * Math.PI ) * radius );
-				if ( facing === 'y' ) vertices.push( Math.cos( i / 32 * Math.PI ) * radius, 0, Math.sin( i / 32 * Math.PI ) * radius );
-				if ( facing === 'z' ) vertices.push( Math.sin( i / 32 * Math.PI ) * radius, Math.cos( i / 32 * Math.PI ) * radius, 0 );
+				}
 
 			}
 
-			geometry.addAttribute( 'position', new THREE.Float32BufferAttribute( vertices, 3 ) );
-			return geometry;
+		} else if ( mode === 'scale' ) {
 
-		};
+			if ( axis.search( 'XYZ' ) !== -1 ) {
 
-		this.handleGizmos = {
+				var d = pointEnd.length() / pointStart.length();
 
-			X: [
-				[ new THREE.Line( new CircleGeometry( 1, 'x', 0.5 ), new GizmoLineMaterial( { color: 0xff0000 } ) ) ]
-			],
+				if ( pointEnd.dot( pointStart ) < 0 ) d *= -1;
 
-			Y: [
-				[ new THREE.Line( new CircleGeometry( 1, 'y', 0.5 ), new GizmoLineMaterial( { color: 0x00ff00 } ) ) ]
-			],
+				_tempVector.set( d, d, d );
 
-			Z: [
-				[ new THREE.Line( new CircleGeometry( 1, 'z', 0.5 ), new GizmoLineMaterial( { color: 0x0000ff } ) ) ]
-			],
+			} else {
 
-			E: [
-				[ new THREE.Line( new CircleGeometry( 1.25, 'z', 1 ), new GizmoLineMaterial( { color: 0xcccc00 } ) ) ]
-			],
+				_tempVector.copy( pointEnd ).divide( pointStart );
 
-			XYZE: [
-				[ new THREE.Line( new CircleGeometry( 1, 'z', 1 ), new GizmoLineMaterial( { color: 0x787878 } ) ) ]
-			]
+				if ( axis.search( 'X' ) === -1 ) {
+					_tempVector.x = 1;
+				}
+				if ( axis.search( 'Y' ) === -1 ) {
+					_tempVector.y = 1;
+				}
+				if ( axis.search( 'Z' ) === -1 ) {
+					_tempVector.z = 1;
+				}
 
-		};
+			}
 
-		this.pickerGizmos = {
+			// Apply scale
 
-			X: [
-				[ new THREE.Mesh( new THREE.TorusBufferGeometry( 1, 0.12, 4, 12, Math.PI ), pickerMaterial ), [ 0, 0, 0 ], [ 0, - Math.PI / 2, - Math.PI / 2 ] ]
-			],
+			object.scale.copy( _scaleStart ).multiply( _tempVector );
 
-			Y: [
-				[ new THREE.Mesh( new THREE.TorusBufferGeometry( 1, 0.12, 4, 12, Math.PI ), pickerMaterial ), [ 0, 0, 0 ], [ Math.PI / 2, 0, 0 ] ]
-			],
+		} else if ( mode === 'rotate' ) {
 
-			Z: [
-				[ new THREE.Mesh( new THREE.TorusBufferGeometry( 1, 0.12, 4, 12, Math.PI ), pickerMaterial ), [ 0, 0, 0 ], [ 0, 0, - Math.PI / 2 ] ]
-			],
+			var ROTATION_SPEED = 20 / worldPosition.distanceTo( _tempVector.setFromMatrixPosition( this.camera.matrixWorld ) );
 
-			E: [
-				[ new THREE.Mesh( new THREE.TorusBufferGeometry( 1.25, 0.12, 2, 24 ), pickerMaterial ) ]
-			],
+			var quaternion = this.space === "local" ? worldQuaternion : _identityQuaternion;
 
-			XYZE: [
-				[ new THREE.Mesh( new THREE.TorusBufferGeometry( 1, 0.12, 2, 24 ), pickerMaterial ) ]
-			]
+			var unit = _unit[ axis ];
 
-		};
+			if ( axis === 'E' ) {
 
-		this.pickerGizmos.XYZE[ 0 ][ 0 ].visible = false; // disable XYZE picker gizmo
+				_tempVector.copy( pointEnd ).cross( pointStart );
+				rotationAxis.copy( eye );
+				rotationAngle = pointEnd.angleTo( pointStart ) * ( _tempVector.dot( eye ) < 0 ? 1 : -1 );
 
-		this.setActivePlane = function ( axis ) {
+			} else if ( axis === 'XYZE' ) {
 
-			if ( axis === "E" ) this.activePlane = this.planes[ "XYZE" ];
+				_tempVector.copy( pointEnd ).sub( pointStart ).cross( eye ).normalize();
+				rotationAxis.copy( _tempVector );
+				rotationAngle = pointEnd.sub( pointStart ).dot( _tempVector.cross( eye ) ) * ROTATION_SPEED;
 
-			if ( axis === "X" ) this.activePlane = this.planes[ "YZ" ];
+			} else if ( axis === 'X' || axis === 'Y' || axis === 'Z' ) {
 
-			if ( axis === "Y" ) this.activePlane = this.planes[ "XZ" ];
+				_alignVector.copy( unit ).applyQuaternion( quaternion );
 
-			if ( axis === "Z" ) this.activePlane = this.planes[ "XY" ];
+				rotationAxis.copy( unit );
 
-		};
+				_tempVector = unit.clone();
+				_tempVector2 = pointEnd.clone().sub( pointStart );
+				if ( space === 'local' ) {
+					_tempVector.applyQuaternion( quaternion );
+					_tempVector2.applyQuaternion( worldQuaternionStart );
+				}
+				rotationAngle = _tempVector2.dot( _tempVector.cross( eye ).normalize() ) * ROTATION_SPEED;
 
-		this.update = function ( rotation, eye2 ) {
+			}
 
-			THREE.TransformGizmo.prototype.update.apply( this, arguments );
+			// Apply rotation snap
 
-			var tempMatrix = new THREE.Matrix4();
-			var worldRotation = new THREE.Euler( 0, 0, 1 );
-			var tempQuaternion = new THREE.Quaternion();
-			var unitX = new THREE.Vector3( 1, 0, 0 );
-			var unitY = new THREE.Vector3( 0, 1, 0 );
-			var unitZ = new THREE.Vector3( 0, 0, 1 );
-			var quaternionX = new THREE.Quaternion();
-			var quaternionY = new THREE.Quaternion();
-			var quaternionZ = new THREE.Quaternion();
-			var eye = eye2.clone();
+			if ( this.rotationSnap ) rotationAngle = Math.round( rotationAngle / this.rotationSnap ) * this.rotationSnap;
 
-			worldRotation.copy( this.planes[ "XY" ].rotation );
-			tempQuaternion.setFromEuler( worldRotation );
+			this.rotationAngle = rotationAngle;
 
-			tempMatrix.makeRotationFromQuaternion( tempQuaternion ).getInverse( tempMatrix );
-			eye.applyMatrix4( tempMatrix );
+			// Apply rotate
 
-			this.traverse( function( child ) {
+			if ( space === 'local' ) {
 
-				tempQuaternion.setFromEuler( worldRotation );
+				object.quaternion.copy( _quaternionStart );
+				object.quaternion.multiply( _tempQuaternion.setFromAxisAngle( rotationAxis, rotationAngle ) );
 
-				if ( child.name === "X" ) {
+			} else {
 
-					quaternionX.setFromAxisAngle( unitX, Math.atan2( - eye.y, eye.z ) );
-					tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionX );
-					child.quaternion.copy( tempQuaternion );
+				object.quaternion.copy( _tempQuaternion.setFromAxisAngle( rotationAxis, rotationAngle ) );
+				object.quaternion.multiply( _quaternionStart );
 
-				}
+			}
 
-				if ( child.name === "Y" ) {
+		}
 
-					quaternionY.setFromAxisAngle( unitY, Math.atan2( eye.x, eye.z ) );
-					tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionY );
-					child.quaternion.copy( tempQuaternion );
+		this.dispatchEvent( changeEvent );
+		this.dispatchEvent( objectChangeEvent );
 
-				}
+	}
 
-				if ( child.name === "Z" ) {
+	this.pointerUp = function( pointer ) {
 
-					quaternionZ.setFromAxisAngle( unitZ, Math.atan2( eye.y, eye.x ) );
-					tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionZ );
-					child.quaternion.copy( tempQuaternion );
+		if ( pointer.button !== undefined && pointer.button !== 0 ) return;
 
-				}
+		if ( this.dragging && ( this.axis !== null ) ) {
 
-			} );
+			mouseUpEvent.mode = this.mode;
+			this.dispatchEvent( mouseUpEvent );
 
-		};
+		}
 
-		this.init();
+		this.dragging = false;
 
-	};
+		if ( pointer.button === undefined ) this.axis = null;
 
-	THREE.TransformGizmoRotate.prototype = Object.create( THREE.TransformGizmo.prototype );
-	THREE.TransformGizmoRotate.prototype.constructor = THREE.TransformGizmoRotate;
+	}
 
-	THREE.TransformGizmoScale = function () {
+	// normalize mouse / touch pointer and remap {x,y} to view space.
 
-		THREE.TransformGizmo.call( this );
+	function getPointer( event ) {
 
-		var arrowGeometry = new THREE.BoxBufferGeometry( 0.125, 0.125, 0.125 );
-		arrowGeometry.translate( 0, 0.5, 0 );
+		var pointer = event.changedTouches ? event.changedTouches[ 0 ] : event;
 
-		var lineXGeometry = new THREE.BufferGeometry();
-		lineXGeometry.addAttribute( 'position', new THREE.Float32BufferAttribute( [ 0, 0, 0,  1, 0, 0 ], 3 ) );
+		var rect = domElement.getBoundingClientRect();
 
-		var lineYGeometry = new THREE.BufferGeometry();
-		lineYGeometry.addAttribute( 'position', new THREE.Float32BufferAttribute( [ 0, 0, 0,  0, 1, 0 ], 3 ) );
+		return {
+			x: ( pointer.clientX - rect.left ) / rect.width * 2 - 1,
+			y: - ( pointer.clientY - rect.top ) / rect.height * 2 + 1,
+			button: event.button
+		}
 
-		var lineZGeometry = new THREE.BufferGeometry();
-		lineZGeometry.addAttribute( 'position', new THREE.Float32BufferAttribute( [ 0, 0, 0,  0, 0, 1 ], 3 ) );
+	}
 
-		this.handleGizmos = {
+	// mouse / touch event handlers
 
-			X: [
-				[ new THREE.Mesh( arrowGeometry, new GizmoMaterial( { color: 0xff0000 } ) ), [ 0.5, 0, 0 ], [ 0, 0, - Math.PI / 2 ] ],
-				[ new THREE.Line( lineXGeometry, new GizmoLineMaterial( { color: 0xff0000 } ) ) ]
-			],
+	function onContext( event ) {
 
-			Y: [
-				[ new THREE.Mesh( arrowGeometry, new GizmoMaterial( { color: 0x00ff00 } ) ), [ 0, 0.5, 0 ] ],
-				[ new THREE.Line( lineYGeometry, new GizmoLineMaterial( { color: 0x00ff00 } ) ) ]
-			],
+		event.preventDefault();
 
-			Z: [
-				[ new THREE.Mesh( arrowGeometry, new GizmoMaterial( { color: 0x0000ff } ) ), [ 0, 0, 0.5 ], [ Math.PI / 2, 0, 0 ] ],
-				[ new THREE.Line( lineZGeometry, new GizmoLineMaterial( { color: 0x0000ff } ) ) ]
-			],
+	}
 
-			XYZ: [
-				[ new THREE.Mesh( new THREE.BoxBufferGeometry( 0.125, 0.125, 0.125 ), new GizmoMaterial( { color: 0xffffff, opacity: 0.25 } ) ) ]
-			]
+	function onPointerHover( event ) {
 
-		};
+		// event.preventDefault();
 
-		this.pickerGizmos = {
+		scope.pointerHover( getPointer( event ) );
 
-			X: [
-				[ new THREE.Mesh( new THREE.CylinderBufferGeometry( 0.2, 0, 1, 4, 1, false ), pickerMaterial ), [ 0.6, 0, 0 ], [ 0, 0, - Math.PI / 2 ] ]
-			],
+	}
 
-			Y: [
-				[ new THREE.Mesh( new THREE.CylinderBufferGeometry( 0.2, 0, 1, 4, 1, false ), pickerMaterial ), [ 0, 0.6, 0 ] ]
-			],
+	function onPointerDown( event ) {
 
-			Z: [
-				[ new THREE.Mesh( new THREE.CylinderBufferGeometry( 0.2, 0, 1, 4, 1, false ), pickerMaterial ), [ 0, 0, 0.6 ], [ Math.PI / 2, 0, 0 ] ]
-			],
+		event.preventDefault();
+		event.stopPropagation();
 
-			XYZ: [
-				[ new THREE.Mesh( new THREE.BoxBufferGeometry( 0.4, 0.4, 0.4 ), pickerMaterial ) ]
-			]
+		scope.pointerHover( getPointer( event ) );
+		scope.pointerDown( getPointer( event ) );
 
-		};
+	}
 
-		this.setActivePlane = function ( axis, eye ) {
+	function onPointerMove( event ) {
 
-			var tempMatrix = new THREE.Matrix4();
-			eye.applyMatrix4( tempMatrix.getInverse( tempMatrix.extractRotation( this.planes[ "XY" ].matrixWorld ) ) );
+		event.preventDefault();
+		event.stopPropagation();
 
-			if ( axis === "X" ) {
+		scope.pointerMove( getPointer( event ) );
 
-				this.activePlane = this.planes[ "XY" ];
-				if ( Math.abs( eye.y ) > Math.abs( eye.z ) ) this.activePlane = this.planes[ "XZ" ];
+	}
 
-			}
+	function onPointerUp( event ) {
 
-			if ( axis === "Y" ) {
+		event.preventDefault(); // Prevent MouseEvent on mobile
 
-				this.activePlane = this.planes[ "XY" ];
-				if ( Math.abs( eye.x ) > Math.abs( eye.z ) ) this.activePlane = this.planes[ "YZ" ];
+		scope.pointerUp( getPointer( event ) );
 
-			}
+	}
 
-			if ( axis === "Z" ) {
+	// TODO: depricate
 
-				this.activePlane = this.planes[ "XZ" ];
-				if ( Math.abs( eye.x ) > Math.abs( eye.y ) ) this.activePlane = this.planes[ "YZ" ];
+	this.getMode = function () {
 
-			}
+		return scope.mode;
 
-			if ( axis === "XYZ" ) this.activePlane = this.planes[ "XYZE" ];
+	};
 
-		};
+	this.setMode = function ( mode ) {
 
-		this.init();
+		scope.mode = mode;
 
 	};
 
-	THREE.TransformGizmoScale.prototype = Object.create( THREE.TransformGizmo.prototype );
-	THREE.TransformGizmoScale.prototype.constructor = THREE.TransformGizmoScale;
+	this.setTranslationSnap = function ( translationSnap ) {
 
-	THREE.TransformControls = function ( camera, domElement ) {
+		scope.translationSnap = translationSnap;
 
-		// TODO: Make non-uniform scale and rotate play nice in hierarchies
-		// TODO: ADD RXYZ contol
+	};
 
-		THREE.Object3D.call( this );
+	this.setRotationSnap = function ( rotationSnap ) {
 
-		domElement = ( domElement !== undefined ) ? domElement : document;
+		scope.rotationSnap = rotationSnap;
 
-		this.object = undefined;
-		this.visible = false;
-		this.translationSnap = null;
-		this.rotationSnap = null;
-		this.space = "world";
-		this.size = 1;
-		this.axis = null;
+	};
 
-		var scope = this;
+	this.setSize = function ( size ) {
 
-		var _mode = "translate";
-		var _dragging = false;
-		var _gizmo = {
+		scope.size = size;
 
-			"translate": new THREE.TransformGizmoTranslate(),
-			"rotate": new THREE.TransformGizmoRotate(),
-			"scale": new THREE.TransformGizmoScale()
-		};
+	};
 
-		for ( var type in _gizmo ) {
+	this.setSpace = function ( space ) {
 
-			var gizmoObj = _gizmo[ type ];
+		scope.space = space;
 
-			gizmoObj.visible = ( type === _mode );
-			this.add( gizmoObj );
+	};
 
-		}
+	this.update = function () {
 
-		var changeEvent = { type: "change" };
-		var mouseDownEvent = { type: "mouseDown" };
-		var mouseUpEvent = { type: "mouseUp", mode: _mode };
-		var objectChangeEvent = { type: "objectChange" };
+		console.warn( 'THREE.TransformControls: update function has been depricated.' );
 
-		var ray = new THREE.Raycaster();
-		var pointerVector = new THREE.Vector2();
+	}
 
-		var point = new THREE.Vector3();
-		var offset = new THREE.Vector3();
+};
 
-		var rotation = new THREE.Vector3();
-		var offsetRotation = new THREE.Vector3();
-		var scale = 1;
+THREE.TransformControls.prototype = Object.assign( Object.create( THREE.Object3D.prototype ), {
 
-		var lookAtMatrix = new THREE.Matrix4();
-		var eye = new THREE.Vector3();
+  constructor: THREE.TransformControls,
 
-		var tempMatrix = new THREE.Matrix4();
-		var tempVector = new THREE.Vector3();
-		var tempQuaternion = new THREE.Quaternion();
-		var unitX = new THREE.Vector3( 1, 0, 0 );
-		var unitY = new THREE.Vector3( 0, 1, 0 );
-		var unitZ = new THREE.Vector3( 0, 0, 1 );
+  isTransformControls: true
 
-		var quaternionXYZ = new THREE.Quaternion();
-		var quaternionX = new THREE.Quaternion();
-		var quaternionY = new THREE.Quaternion();
-		var quaternionZ = new THREE.Quaternion();
-		var quaternionE = new THREE.Quaternion();
+} );
 
-		var oldPosition = new THREE.Vector3();
-		var oldScale = new THREE.Vector3();
-		var oldRotationMatrix = new THREE.Matrix4();
 
-		var parentRotationMatrix = new THREE.Matrix4();
-		var parentScale = new THREE.Vector3();
+THREE.TransformControlsGizmo = function () {
 
-		var worldPosition = new THREE.Vector3();
-		var worldRotation = new THREE.Euler();
-		var worldRotationMatrix = new THREE.Matrix4();
-		var camPosition = new THREE.Vector3();
-		var camRotation = new THREE.Euler();
+	'use strict';
 
-		domElement.addEventListener( "mousedown", onPointerDown, false );
-		domElement.addEventListener( "touchstart", onPointerDown, false );
+	THREE.Object3D.call( this );
 
-		domElement.addEventListener( "mousemove", onPointerHover, false );
-		domElement.addEventListener( "touchmove", onPointerHover, false );
+	this.type = 'TransformControlsGizmo';
 
-		domElement.addEventListener( "mousemove", onPointerMove, false );
-		domElement.addEventListener( "touchmove", onPointerMove, false );
+	// shared materials
 
-		domElement.addEventListener( "mouseup", onPointerUp, false );
-		domElement.addEventListener( "mouseout", onPointerUp, false );
-		domElement.addEventListener( "touchend", onPointerUp, false );
-		domElement.addEventListener( "touchcancel", onPointerUp, false );
-		domElement.addEventListener( "touchleave", onPointerUp, false );
+	var gizmoMaterial = new THREE.MeshBasicMaterial({
+		depthTest: false,
+		depthWrite: false,
+		transparent: true,
+		side: THREE.DoubleSide,
+		fog: false
+	});
 
-		this.dispose = function () {
+	var gizmoLineMaterial = new THREE.LineBasicMaterial({
+		depthTest: false,
+		depthWrite: false,
+		transparent: true,
+		linewidth: 1,
+		fog: false
+	});
 
-			domElement.removeEventListener( "mousedown", onPointerDown );
-			domElement.removeEventListener( "touchstart", onPointerDown );
+	// Make unique material for each axis/color
 
-			domElement.removeEventListener( "mousemove", onPointerHover );
-			domElement.removeEventListener( "touchmove", onPointerHover );
+	var matInvisible = gizmoMaterial.clone();
+	matInvisible.opacity = 0.15;
 
-			domElement.removeEventListener( "mousemove", onPointerMove );
-			domElement.removeEventListener( "touchmove", onPointerMove );
+	var matHelper = gizmoMaterial.clone();
+	matHelper.opacity = 0.33;
 
-			domElement.removeEventListener( "mouseup", onPointerUp );
-			domElement.removeEventListener( "mouseout", onPointerUp );
-			domElement.removeEventListener( "touchend", onPointerUp );
-			domElement.removeEventListener( "touchcancel", onPointerUp );
-			domElement.removeEventListener( "touchleave", onPointerUp );
+	var matRed = gizmoMaterial.clone();
+	matRed.color.set( 0xff0000 );
 
-		};
+	var matGreen = gizmoMaterial.clone();
+	matGreen.color.set( 0x00ff00 );
 
-		this.attach = function ( object ) {
+	var matBlue = gizmoMaterial.clone();
+	matBlue.color.set( 0x0000ff );
 
-			this.object = object;
-			this.visible = true;
-			this.update();
+	var matWhiteTransperent = gizmoMaterial.clone();
+	matWhiteTransperent.opacity = 0.25;
 
-		};
+	var matYellowTransparent = matWhiteTransperent.clone();
+	matYellowTransparent.color.set( 0xffff00 );
 
-		this.detach = function () {
+	var matCyanTransparent = matWhiteTransperent.clone();
+	matCyanTransparent.color.set( 0x00ffff );
 
-			this.object = undefined;
-			this.visible = false;
-			this.axis = null;
-
-		};
+	var matMagentaTransparent = matWhiteTransperent.clone();
+	matMagentaTransparent.color.set( 0xff00ff );
 
-		this.getMode = function () {
+	var matYellow = gizmoMaterial.clone();
+	matYellow.color.set( 0xffff00 );
 
-			return _mode;
+	var matLineRed = gizmoLineMaterial.clone();
+	matLineRed.color.set( 0xff0000 );
 
-		};
+	var matLineGreen = gizmoLineMaterial.clone();
+	matLineGreen.color.set( 0x00ff00 );
 
-		this.setMode = function ( mode ) {
+	var matLineBlue = gizmoLineMaterial.clone();
+	matLineBlue.color.set( 0x0000ff );
 
-			_mode = mode ? mode : _mode;
+	var matLineCyan = gizmoLineMaterial.clone();
+	matLineCyan.color.set( 0x00ffff );
 
-			if ( _mode === "scale" ) scope.space = "local";
+	var matLineMagenta = gizmoLineMaterial.clone();
+	matLineMagenta.color.set( 0xff00ff );
 
-			for ( var type in _gizmo ) _gizmo[ type ].visible = ( type === _mode );
+	var matLineBlue = gizmoLineMaterial.clone();
+	matLineBlue.color.set( 0x0000ff );
 
-			this.update();
-			scope.dispatchEvent( changeEvent );
+	var matLineYellow = gizmoLineMaterial.clone();
+	matLineYellow.color.set( 0xffff00 );
 
-		};
+	var matLineGray = gizmoLineMaterial.clone();
+	matLineGray.color.set( 0x787878);
 
-		this.setTranslationSnap = function ( translationSnap ) {
+	var matLineYellowTransparent = matLineYellow.clone();
+	matLineYellowTransparent.opacity = 0.25;
 
-			scope.translationSnap = translationSnap;
+	// reusable geometry
 
-		};
+	var arrowGeometry = new THREE.CylinderBufferGeometry( 0, 0.05, 0.2, 12, 1, false);
 
-		this.setRotationSnap = function ( rotationSnap ) {
+	var scaleHandleGeometry = new THREE.BoxBufferGeometry( 0.125, 0.125, 0.125);
 
-			scope.rotationSnap = rotationSnap;
+	var lineGeometry = new THREE.BufferGeometry( );
+	lineGeometry.addAttribute('position', new THREE.Float32BufferAttribute( [ 0, 0, 0,	1, 0, 0 ], 3 ) );
 
-		};
+	var CircleGeometry = function( radius, arc ) {
 
-		this.setSize = function ( size ) {
+		var geometry = new THREE.BufferGeometry( );
+		var vertices = [];
 
-			scope.size = size;
-			this.update();
-			scope.dispatchEvent( changeEvent );
+		for ( var i = 0; i <= 64 * arc; ++i ) {
 
-		};
+			vertices.push( 0, Math.cos( i / 32 * Math.PI ) * radius, Math.sin( i / 32 * Math.PI ) * radius );
 
-		this.setSpace = function ( space ) {
+		}
 
-			scope.space = space;
-			this.update();
-			scope.dispatchEvent( changeEvent );
+		geometry.addAttribute('position', new THREE.Float32BufferAttribute( vertices, 3 ) );
 
-		};
+		return geometry;
 
-		this.update = function () {
+	};
 
-			if ( scope.object === undefined ) return;
+	// Special geometry for transform helper. If scaled with position vector it spans from [0,0,0] to position
 
-			scope.object.updateMatrixWorld();
-			worldPosition.setFromMatrixPosition( scope.object.matrixWorld );
-			worldRotation.setFromRotationMatrix( tempMatrix.extractRotation( scope.object.matrixWorld ) );
+	var TranslateHelperGeometry = function( radius, arc ) {
 
-			camera.updateMatrixWorld();
-			camPosition.setFromMatrixPosition( camera.matrixWorld );
-			camRotation.setFromRotationMatrix( tempMatrix.extractRotation( camera.matrixWorld ) );
+		var geometry = new THREE.BufferGeometry()
 
-			scale = worldPosition.distanceTo( camPosition ) / 6 * scope.size;
-			this.position.copy( worldPosition );
-			this.scale.set( scale, scale, scale );
+		geometry.addAttribute('position', new THREE.Float32BufferAttribute( [ 0, 0, 0, 1, 1, 1 ], 3 ) );
 
-			if ( camera instanceof THREE.PerspectiveCamera ) {
+		return geometry;
 
-				eye.copy( camPosition ).sub( worldPosition ).normalize();
+	};
 
-			} else if ( camera instanceof THREE.OrthographicCamera ) {
+	// Gizmo definitions - custom hierarchy definitions for setupGizmo() function
+
+	var gizmoTranslate = {
+		X: [
+			[ new THREE.Mesh( arrowGeometry, matRed ), [ 1, 0, 0 ], [ 0, 0, -Math.PI / 2 ], null, 'fwd' ],
+			[ new THREE.Mesh( arrowGeometry, matRed ), [ 1, 0, 0 ], [ 0, 0, Math.PI / 2 ], null, 'bwd' ],
+			[ new THREE.Line( lineGeometry, matLineRed ) ]
+		],
+		Y: [
+			[ new THREE.Mesh( arrowGeometry, matGreen ), [ 0, 1, 0 ], null, null, 'fwd' ],
+			[ new THREE.Mesh( arrowGeometry, matGreen ), [ 0, 1, 0 ], [ Math.PI, 0, 0 ], null, 'bwd' ],
+			[ new THREE.Line( lineGeometry, matLineGreen ), null, [ 0, 0, Math.PI / 2 ] ]
+		],
+		Z: [
+			[ new THREE.Mesh( arrowGeometry, matBlue ), [ 0, 0, 1 ], [ Math.PI / 2, 0, 0 ], null, 'fwd' ],
+			[ new THREE.Mesh( arrowGeometry, matBlue ), [ 0, 0, 1 ], [ -Math.PI / 2, 0, 0 ], null, 'bwd' ],
+			[ new THREE.Line( lineGeometry, matLineBlue ), null, [ 0, -Math.PI / 2, 0 ] ]
+		],
+		XYZ: [
+			[ new THREE.Mesh( new THREE.OctahedronBufferGeometry( 0.1, 0 ), matWhiteTransperent ), [ 0, 0, 0 ], [ 0, 0, 0 ] ]
+		],
+		XY: [
+			[ new THREE.Mesh( new THREE.PlaneBufferGeometry( 0.295, 0.295 ), matYellowTransparent ), [ 0.15, 0.15, 0 ] ],
+			[ new THREE.Line( lineGeometry, matLineYellow ), [ 0.18, 0.3, 0 ], null, [ 0.125, 1, 1 ] ],
+			[ new THREE.Line( lineGeometry, matLineYellow ), [ 0.3, 0.18, 0 ], [ 0, 0, Math.PI / 2 ], [ 0.125, 1, 1 ] ]
+		],
+		YZ: [
+			[ new THREE.Mesh( new THREE.PlaneBufferGeometry( 0.295, 0.295 ), matCyanTransparent ), [ 0, 0.15, 0.15 ], [ 0, Math.PI / 2, 0 ] ],
+			[ new THREE.Line( lineGeometry, matLineCyan ), [ 0, 0.18, 0.3 ], [ 0, 0, Math.PI / 2 ], [ 0.125, 1, 1 ] ],
+			[ new THREE.Line( lineGeometry, matLineCyan ), [ 0, 0.3, 0.18 ], [ 0, -Math.PI / 2, 0 ], [ 0.125, 1, 1 ] ]
+		],
+		XZ: [
+			[ new THREE.Mesh( new THREE.PlaneBufferGeometry( 0.295, 0.295 ), matMagentaTransparent ), [ 0.15, 0, 0.15 ], [ -Math.PI / 2, 0, 0 ] ],
+			[ new THREE.Line( lineGeometry, matLineMagenta ), [ 0.18, 0, 0.3 ], null, [ 0.125, 1, 1 ] ],
+			[ new THREE.Line( lineGeometry, matLineMagenta ), [ 0.3, 0, 0.18 ], [ 0, -Math.PI / 2, 0 ], [ 0.125, 1, 1 ] ]
+		]
+	};
 
-				eye.copy( camPosition ).normalize();
+	var pickerTranslate = {
+		X: [
+			[ new THREE.Mesh( new THREE.CylinderBufferGeometry( 0.2, 0, 1, 4, 1, false ), matInvisible ), [ 0.6, 0, 0 ], [ 0, 0, -Math.PI / 2 ] ]
+		],
+		Y: [
+			[ new THREE.Mesh( new THREE.CylinderBufferGeometry( 0.2, 0, 1, 4, 1, false ), matInvisible ), [ 0, 0.6, 0 ] ]
+		],
+		Z: [
+			[ new THREE.Mesh( new THREE.CylinderBufferGeometry( 0.2, 0, 1, 4, 1, false ), matInvisible ), [ 0, 0, 0.6 ], [ Math.PI / 2, 0, 0 ] ]
+		],
+		XYZ: [
+			[ new THREE.Mesh( new THREE.OctahedronBufferGeometry( 0.2, 0 ), matInvisible ) ]
+		],
+		XY: [
+			[ new THREE.Mesh( new THREE.PlaneBufferGeometry( 0.4, 0.4 ), matInvisible ), [ 0.2, 0.2, 0 ] ]
+		],
+		YZ: [
+			[ new THREE.Mesh( new THREE.PlaneBufferGeometry( 0.4, 0.4 ), matInvisible ), [ 0, 0.2, 0.2 ], [ 0, Math.PI / 2, 0 ] ]
+		],
+		XZ: [
+			[ new THREE.Mesh( new THREE.PlaneBufferGeometry( 0.4, 0.4 ), matInvisible ), [ 0.2, 0, 0.2 ], [ -Math.PI / 2, 0, 0 ] ]
+		]
+	};
 
-			}
+	var helperTranslate = {
+		START: [
+			[ new THREE.Mesh( new THREE.OctahedronBufferGeometry( 0.01, 2 ), matHelper ), null, null, null, 'helper' ]
+		],
+		END: [
+			[ new THREE.Mesh( new THREE.OctahedronBufferGeometry( 0.01, 2 ), matHelper ), null, null, null, 'helper' ]
+		],
+		DELTA: [
+			[ new THREE.Line( TranslateHelperGeometry(), matHelper ), null, null, null, 'helper' ]
+		],
+		X: [
+			[ new THREE.Line( lineGeometry, matHelper.clone() ), [ -1e3, 0, 0 ], null, [ 1e6, 1, 1 ], 'helper' ]
+		],
+		Y: [
+			[ new THREE.Line( lineGeometry, matHelper.clone() ), [ 0, -1e3, 0 ], [ 0, 0, Math.PI / 2 ], [ 1e6, 1, 1 ], 'helper' ]
+		],
+		Z: [
+			[ new THREE.Line( lineGeometry, matHelper.clone() ), [ 0, 0, -1e3 ], [ 0, -Math.PI / 2, 0 ], [ 1e6, 1, 1 ], 'helper' ]
+		]
+	};
 
-			if ( scope.space === "local" ) {
+	var gizmoRotate = {
+		X: [
+			[ new THREE.Line( CircleGeometry( 1, 0.5 ), matLineRed ) ],
+			[ new THREE.Mesh( new THREE.OctahedronBufferGeometry( 0.04, 0 ), matRed ), [ 0, 0, 0.99 ], null, [ 1, 3, 1 ] ],
+		],
+		Y: [
+			[ new THREE.Line( CircleGeometry( 1, 0.5 ), matLineGreen ), null, [ 0, 0, -Math.PI / 2 ] ],
+			[ new THREE.Mesh( new THREE.OctahedronBufferGeometry( 0.04, 0 ), matGreen ), [ 0, 0, 0.99 ], null, [ 3, 1, 1 ] ],
+		],
+		Z: [
+			[ new THREE.Line( CircleGeometry( 1, 0.5 ), matLineBlue ), null, [ 0, Math.PI / 2, 0 ] ],
+			[ new THREE.Mesh( new THREE.OctahedronBufferGeometry( 0.04, 0 ), matBlue ), [ 0.99, 0, 0 ], null, [ 1, 3, 1 ] ],
+		],
+		E: [
+			[ new THREE.Line( CircleGeometry( 1.25, 1 ), matLineYellowTransparent ), null, [ 0, Math.PI / 2, 0 ] ],
+			[ new THREE.Mesh( new THREE.CylinderBufferGeometry( 0.03, 0, 0.15, 4, 1, false ), matLineYellowTransparent ), [ 1.17, 0, 0 ], [ 0, 0, -Math.PI / 2 ], [ 1, 1, 0.001 ]],
+			[ new THREE.Mesh( new THREE.CylinderBufferGeometry( 0.03, 0, 0.15, 4, 1, false ), matLineYellowTransparent ), [ -1.17, 0, 0 ], [ 0, 0, Math.PI / 2 ], [ 1, 1, 0.001 ]],
+			[ new THREE.Mesh( new THREE.CylinderBufferGeometry( 0.03, 0, 0.15, 4, 1, false ), matLineYellowTransparent ), [ 0, -1.17, 0 ], [ Math.PI, 0, 0 ], [ 1, 1, 0.001 ]],
+			[ new THREE.Mesh( new THREE.CylinderBufferGeometry( 0.03, 0, 0.15, 4, 1, false ), matLineYellowTransparent ), [ 0, 1.17, 0 ], [ 0, 0, 0 ], [ 1, 1, 0.001 ]],
+		],
+		XYZE: [
+			[ new THREE.Line( CircleGeometry( 1, 1 ), matLineGray ), null, [ 0, Math.PI / 2, 0 ] ]
+		]
+	};
 
-				_gizmo[ _mode ].update( worldRotation, eye );
+	var helperRotate = {
+		AXIS: [
+			[ new THREE.Line( lineGeometry, matHelper.clone() ), [ -1e3, 0, 0 ], null, [ 1e6, 1, 1 ], 'helper' ]
+		]
+	};
 
-			} else if ( scope.space === "world" ) {
+	var pickerRotate = {
+		X: [
+			[ new THREE.Mesh( new THREE.TorusBufferGeometry( 1, 0.1, 4, 24 ), matInvisible ), [ 0, 0, 0 ], [ 0, -Math.PI / 2, -Math.PI / 2 ] ],
+		],
+		Y: [
+			[ new THREE.Mesh( new THREE.TorusBufferGeometry( 1, 0.1, 4, 24 ), matInvisible ), [ 0, 0, 0 ], [ Math.PI / 2, 0, 0 ] ],
+		],
+		Z: [
+			[ new THREE.Mesh( new THREE.TorusBufferGeometry( 1, 0.1, 4, 24 ), matInvisible ), [ 0, 0, 0 ], [ 0, 0, -Math.PI / 2 ] ],
+		],
+		E: [
+			[ new THREE.Mesh( new THREE.TorusBufferGeometry( 1.25, 0.1, 2, 24 ), matInvisible ) ]
+		],
+		XYZE: [
+			[ new THREE.Mesh( new THREE.SphereBufferGeometry( 0.7, 10, 8 ), matInvisible ) ]
+		]
+	};
 
-				_gizmo[ _mode ].update( new THREE.Euler(), eye );
+	var gizmoScale = {
+		X: [
+			[ new THREE.Mesh( scaleHandleGeometry, matRed ), [ 0.8, 0, 0 ], [ 0, 0, -Math.PI / 2 ] ],
+			[ new THREE.Line( lineGeometry, matLineRed ), null, null, [ 0.8, 1, 1 ] ]
+		],
+		Y: [
+			[ new THREE.Mesh( scaleHandleGeometry, matGreen ), [ 0, 0.8, 0 ] ],
+			[ new THREE.Line( lineGeometry, matLineGreen ), null, [ 0, 0, Math.PI / 2 ], [ 0.8, 1, 1 ] ]
+		],
+		Z: [
+			[ new THREE.Mesh( scaleHandleGeometry, matBlue ), [ 0, 0, 0.8 ], [ Math.PI / 2, 0, 0 ] ],
+			[ new THREE.Line( lineGeometry, matLineBlue ), null, [ 0, -Math.PI / 2, 0 ], [ 0.8, 1, 1 ] ]
+		],
+		XY: [
+			[ new THREE.Mesh( scaleHandleGeometry, matYellowTransparent ), [ 0.85, 0.85, 0 ], null, [ 2, 2, 0.2 ] ],
+			[ new THREE.Line( lineGeometry, matLineYellow ), [ 0.855, 0.98, 0 ], null, [ 0.125, 1, 1 ] ],
+			[ new THREE.Line( lineGeometry, matLineYellow ), [ 0.98, 0.855, 0 ], [ 0, 0, Math.PI / 2 ], [ 0.125, 1, 1 ] ]
+		],
+		YZ: [
+			[ new THREE.Mesh( scaleHandleGeometry, matCyanTransparent ), [ 0, 0.85, 0.85 ], null, [ 0.2, 2, 2 ] ],
+			[ new THREE.Line( lineGeometry, matLineCyan ), [ 0, 0.855, 0.98 ], [ 0, 0, Math.PI / 2 ], [ 0.125, 1, 1 ] ],
+			[ new THREE.Line( lineGeometry, matLineCyan ), [ 0, 0.98, 0.855 ], [ 0, -Math.PI / 2, 0 ], [ 0.125, 1, 1 ] ]
+		],
+		XZ: [
+			[ new THREE.Mesh( scaleHandleGeometry, matMagentaTransparent ), [ 0.85, 0, 0.85 ], null, [ 2, 0.2, 2 ] ],
+			[ new THREE.Line( lineGeometry, matLineMagenta ), [ 0.855, 0, 0.98 ], null, [ 0.125, 1, 1 ] ],
+			[ new THREE.Line( lineGeometry, matLineMagenta ), [ 0.98, 0, 0.855 ], [ 0, -Math.PI / 2, 0 ], [ 0.125, 1, 1 ] ]
+		],
+		XYZX: [
+			[ new THREE.Mesh( new THREE.BoxBufferGeometry( 0.125, 0.125, 0.125 ), matWhiteTransperent ), [ 1.1, 0, 0 ] ],
+		],
+		XYZY: [
+			[ new THREE.Mesh( new THREE.BoxBufferGeometry( 0.125, 0.125, 0.125 ), matWhiteTransperent ), [ 0, 1.1, 0 ] ],
+		],
+		XYZZ: [
+			[ new THREE.Mesh( new THREE.BoxBufferGeometry( 0.125, 0.125, 0.125 ), matWhiteTransperent ), [ 0, 0, 1.1 ] ],
+		]
+	};
 
-			}
+	var pickerScale = {
+		X: [
+			[ new THREE.Mesh( new THREE.CylinderBufferGeometry( 0.2, 0, 0.8, 4, 1, false ), matInvisible ), [ 0.5, 0, 0 ], [ 0, 0, -Math.PI / 2 ] ]
+		],
+		Y: [
+			[ new THREE.Mesh( new THREE.CylinderBufferGeometry( 0.2, 0, 0.8, 4, 1, false ), matInvisible ), [ 0, 0.5, 0 ] ]
+		],
+		Z: [
+			[ new THREE.Mesh( new THREE.CylinderBufferGeometry( 0.2, 0, 0.8, 4, 1, false ), matInvisible ), [ 0, 0, 0.5 ], [ Math.PI / 2, 0, 0 ] ]
+		],
+		XY: [
+			[ new THREE.Mesh( scaleHandleGeometry, matInvisible ), [ 0.85, 0.85, 0 ], null, [ 3, 3, 0.2 ] ],
+		],
+		YZ: [
+			[ new THREE.Mesh( scaleHandleGeometry, matInvisible ), [ 0, 0.85, 0.85 ], null, [ 0.2, 3, 3 ] ],
+		],
+		XZ: [
+			[ new THREE.Mesh( scaleHandleGeometry, matInvisible ), [ 0.85, 0, 0.85 ], null, [ 3, 0.2, 3 ] ],
+		],
+		XYZX: [
+			[ new THREE.Mesh( new THREE.BoxBufferGeometry( 0.2, 0.2, 0.2 ), matInvisible ), [ 1.1, 0, 0 ] ],
+		],
+		XYZY: [
+			[ new THREE.Mesh( new THREE.BoxBufferGeometry( 0.2, 0.2, 0.2 ), matInvisible ), [ 0, 1.1, 0 ] ],
+		],
+		XYZZ: [
+			[ new THREE.Mesh( new THREE.BoxBufferGeometry( 0.2, 0.2, 0.2 ), matInvisible ), [ 0, 0, 1.1 ] ],
+		]
+	};
 
-			_gizmo[ _mode ].highlight( scope.axis );
+	var helperScale = {
+		X: [
+			[ new THREE.Line( lineGeometry, matHelper.clone() ), [ -1e3, 0, 0 ], null, [ 1e6, 1, 1 ], 'helper' ]
+		],
+		Y: [
+			[ new THREE.Line( lineGeometry, matHelper.clone() ), [ 0, -1e3, 0 ], [ 0, 0, Math.PI / 2 ], [ 1e6, 1, 1 ], 'helper' ]
+		],
+		Z: [
+			[ new THREE.Line( lineGeometry, matHelper.clone() ), [ 0, 0, -1e3 ], [ 0, -Math.PI / 2, 0 ], [ 1e6, 1, 1 ], 'helper' ]
+		]
+	};
 
-		};
+	// Creates an Object3D with gizmos described in custom hierarchy definition.
 
-		function onPointerHover( event ) {
+	var setupGizmo = function( gizmoMap ) {
 
-			if ( scope.object === undefined || _dragging === true || ( event.button !== undefined && event.button !== 0 ) ) return;
+		var gizmo = new THREE.Object3D();
 
-			var pointer = event.changedTouches ? event.changedTouches[ 0 ] : event;
+		for ( var name in gizmoMap ) {
 
-			var intersect = intersectObjects( pointer, _gizmo[ _mode ].pickers.children );
+			for ( var i = gizmoMap[ name ].length; i --; ) {
 
-			var axis = null;
+				var object = gizmoMap[ name ][ i ][ 0 ].clone();
+				var position = gizmoMap[ name ][ i ][ 1 ];
+				var rotation = gizmoMap[ name ][ i ][ 2 ];
+				var scale = gizmoMap[ name ][ i ][ 3 ];
+				var tag = gizmoMap[ name ][ i ][ 4 ];
 
-			if ( intersect ) {
+				// name and tag properties are essential for picking and updating logic.
+				object.name = name;
+				object.tag = tag;
 
-				axis = intersect.object.name;
+				if (position) {
+					object.position.set(position[ 0 ], position[ 1 ], position[ 2 ]);
+				}
+				if (rotation) {
+					object.rotation.set(rotation[ 0 ], rotation[ 1 ], rotation[ 2 ]);
+				}
+				if (scale) {
+					object.scale.set(scale[ 0 ], scale[ 1 ], scale[ 2 ]);
+				}
 
-				event.preventDefault();
+				object.updateMatrix();
 
-			}
+				var tempGeometry = object.geometry.clone();
+				tempGeometry.applyMatrix(object.matrix);
+				object.geometry = tempGeometry;
 
-			if ( scope.axis !== axis ) {
+				object.position.set( 0, 0, 0 );
+				object.rotation.set( 0, 0, 0 );
+				object.scale.set(1, 1, 1);
 
-				scope.axis = axis;
-				scope.update();
-				scope.dispatchEvent( changeEvent );
+				gizmo.add(object);
 
 			}
 
 		}
 
-		function onPointerDown( event ) {
+		return gizmo;
+
+	};
 
-			if ( scope.object === undefined || _dragging === true || ( event.button !== undefined && event.button !== 0 ) ) return;
+	// Reusable utility variables
 
-			var pointer = event.changedTouches ? event.changedTouches[ 0 ] : event;
+	var tempVector = new THREE.Vector3( 0, 0, 0 );
+	var tempEuler = new THREE.Euler();
+	var alignVector = new THREE.Vector3( 0, 1, 0 );
+	var zeroVector = new THREE.Vector3( 0, 0, 0 );
+	var lookAtMatrix = new THREE.Matrix4();
+	var tempQuaternion = new THREE.Quaternion();
+	var tempQuaternion2 = new THREE.Quaternion();
+	var identityQuaternion = new THREE.Quaternion();
 
-			if ( pointer.button === 0 || pointer.button === undefined ) {
+	var unitX = new THREE.Vector3( 1, 0, 0 );
+	var unitY = new THREE.Vector3( 0, 1, 0 );
+	var unitZ = new THREE.Vector3( 0, 0, 1 );
 
-				var intersect = intersectObjects( pointer, _gizmo[ _mode ].pickers.children );
+	// Gizmo creation
 
-				if ( intersect ) {
+	this.gizmo = {};
+	this.picker = {};
+	this.helper = {};
 
-					event.preventDefault();
-					event.stopPropagation();
+	this.add( this.gizmo[ "translate" ] = setupGizmo( gizmoTranslate ) );
+	this.add( this.gizmo[ "rotate" ] = setupGizmo( gizmoRotate ) );
+	this.add( this.gizmo[ "scale" ] = setupGizmo( gizmoScale ) );
+	this.add( this.picker[ "translate" ] = setupGizmo( pickerTranslate ) );
+	this.add( this.picker[ "rotate" ] = setupGizmo( pickerRotate ) );
+	this.add( this.picker[ "scale" ] = setupGizmo( pickerScale ) );
+	this.add( this.helper[ "translate" ] = setupGizmo( helperTranslate ) );
+	this.add( this.helper[ "rotate" ] = setupGizmo( helperRotate ) );
+	this.add( this.helper[ "scale" ] = setupGizmo( helperScale ) );
 
-					scope.axis = intersect.object.name;
+	// Pickers should be hidden always
 
-					scope.dispatchEvent( mouseDownEvent );
+	this.picker[ "translate" ].visible = false;
+	this.picker[ "rotate" ].visible = false;
+	this.picker[ "scale" ].visible = false;
 
-					scope.update();
+	// updateMatrixWorld will update transformations and appearance of individual handles
 
-					eye.copy( camPosition ).sub( worldPosition ).normalize();
+	this.updateMatrixWorld = function () {
 
-					_gizmo[ _mode ].setActivePlane( scope.axis, eye );
+		var space = this.space;
 
-					var planeIntersect = intersectObjects( pointer, [ _gizmo[ _mode ].activePlane ] );
+		if ( this.mode === 'scale' ) space = 'local'; // scale always oriented to local rotation
 
-					if ( planeIntersect ) {
+		var quaternion = space === "local" ? this.worldQuaternion : identityQuaternion;
 
-						oldPosition.copy( scope.object.position );
-						oldScale.copy( scope.object.scale );
+		// Show only gizmos for current transform mode
 
-						oldRotationMatrix.extractRotation( scope.object.matrix );
-						worldRotationMatrix.extractRotation( scope.object.matrixWorld );
+		this.gizmo[ "translate" ].visible = this.mode === "translate";
+		this.gizmo[ "rotate" ].visible = this.mode === "rotate";
+		this.gizmo[ "scale" ].visible = this.mode === "scale";
 
-						parentRotationMatrix.extractRotation( scope.object.parent.matrixWorld );
-						parentScale.setFromMatrixScale( tempMatrix.getInverse( scope.object.parent.matrixWorld ) );
+		this.helper[ "translate" ].visible = this.mode === "translate";
+		this.helper[ "rotate" ].visible = this.mode === "rotate";
+		this.helper[ "scale" ].visible = this.mode === "scale";
 
-						offset.copy( planeIntersect.point );
 
-					}
+		var handles = [];
+		handles = handles.concat( this.picker[ this.mode ].children );
+		handles = handles.concat( this.gizmo[ this.mode ].children );
+		handles = handles.concat( this.helper[ this.mode ].children );
 
-				}
+		for ( var i = 0; i < handles.length; i++ ) {
 
-			}
+			var handle = handles[i];
 
-			_dragging = true;
+			// hide aligned to camera
 
-		}
+			handle.visible = true;
+			handle.rotation.set( 0, 0, 0 );
+			handle.position.copy( this.worldPosition );
 
-		function onPointerMove( event ) {
+			var eyeDistance = this.worldPosition.distanceTo( this.cameraPosition);
+			handle.scale.set( 1, 1, 1 ).multiplyScalar( eyeDistance * this.size / 7 );
 
-			if ( scope.object === undefined || scope.axis === null || _dragging === false || ( event.button !== undefined && event.button !== 0 ) ) return;
+			// TODO: simplify helpers and consider decoupling from gizmo
 
-			var pointer = event.changedTouches ? event.changedTouches[ 0 ] : event;
+			if ( handle.tag === 'helper' ) {
 
-			var planeIntersect = intersectObjects( pointer, [ _gizmo[ _mode ].activePlane ] );
+				handle.visible = false;
 
-			if ( planeIntersect === false ) return;
+				if ( handle.name === 'AXIS' ) {
 
-			event.preventDefault();
-			event.stopPropagation();
+					handle.position.copy( this.worldPositionStart );
+					handle.visible = !!this.axis;
 
-			point.copy( planeIntersect.point );
+					if ( this.axis === 'X' ) {
 
-			if ( _mode === "translate" ) {
+						tempQuaternion.setFromEuler( tempEuler.set( 0, 0, 0 ) );
+						handle.quaternion.copy( quaternion ).multiply( tempQuaternion );
 
-				point.sub( offset );
-				point.multiply( parentScale );
+						if ( Math.abs( alignVector.copy( unitX ).applyQuaternion( quaternion ).dot( this.eye ) ) > 0.9 ) {
+							handle.visible = false;
+						}
 
-				if ( scope.space === "local" ) {
+					}
 
-					point.applyMatrix4( tempMatrix.getInverse( worldRotationMatrix ) );
+					if ( this.axis === 'Y' ) {
 
-					if ( scope.axis.search( "X" ) === - 1 ) point.x = 0;
-					if ( scope.axis.search( "Y" ) === - 1 ) point.y = 0;
-					if ( scope.axis.search( "Z" ) === - 1 ) point.z = 0;
+						tempQuaternion.setFromEuler( tempEuler.set( 0, 0, Math.PI / 2 ) );
+						handle.quaternion.copy( quaternion ).multiply( tempQuaternion );
 
-					point.applyMatrix4( oldRotationMatrix );
+						if ( Math.abs( alignVector.copy( unitY ).applyQuaternion( quaternion ).dot( this.eye ) ) > 0.9 ) {
+							handle.visible = false;
+						}
 
-					scope.object.position.copy( oldPosition );
-					scope.object.position.add( point );
+					}
 
-				}
+					if ( this.axis === 'Z' ) {
 
-				if ( scope.space === "world" || scope.axis.search( "XYZ" ) !== - 1 ) {
+						tempQuaternion.setFromEuler( tempEuler.set( 0, Math.PI / 2, 0 ) );
+						handle.quaternion.copy( quaternion ).multiply( tempQuaternion );
 
-					if ( scope.axis.search( "X" ) === - 1 ) point.x = 0;
-					if ( scope.axis.search( "Y" ) === - 1 ) point.y = 0;
-					if ( scope.axis.search( "Z" ) === - 1 ) point.z = 0;
+						if ( Math.abs( alignVector.copy( unitZ ).applyQuaternion( quaternion ).dot( this.eye ) ) > 0.9 ) {
+							handle.visible = false;
+						}
 
-					point.applyMatrix4( tempMatrix.getInverse( parentRotationMatrix ) );
+					}
 
-					scope.object.position.copy( oldPosition );
-					scope.object.position.add( point );
+					if ( this.axis === 'XYZE' ) {
 
-				}
+						tempQuaternion.setFromEuler( tempEuler.set( 0, Math.PI / 2, 0 ) );
+						alignVector.copy( this.rotationAxis );
+						handle.quaternion.setFromRotationMatrix( lookAtMatrix.lookAt( zeroVector, alignVector, unitY ) );
+						handle.quaternion.multiply( tempQuaternion );
+						handle.visible = this.dragging;
 
-				if ( scope.translationSnap !== null ) {
+					}
 
-					if ( scope.space === "local" ) {
+					if ( this.axis === 'E' ) {
 
-						scope.object.position.applyMatrix4( tempMatrix.getInverse( worldRotationMatrix ) );
+						handle.visible = false;
 
 					}
 
-					if ( scope.axis.search( "X" ) !== - 1 ) scope.object.position.x = Math.round( scope.object.position.x / scope.translationSnap ) * scope.translationSnap;
-					if ( scope.axis.search( "Y" ) !== - 1 ) scope.object.position.y = Math.round( scope.object.position.y / scope.translationSnap ) * scope.translationSnap;
-					if ( scope.axis.search( "Z" ) !== - 1 ) scope.object.position.z = Math.round( scope.object.position.z / scope.translationSnap ) * scope.translationSnap;
 
-					if ( scope.space === "local" ) {
+				} else if ( handle.name === 'START' ) {
 
-						scope.object.position.applyMatrix4( worldRotationMatrix );
+					handle.position.copy( this.worldPositionStart );
+					handle.visible = this.dragging;
 
-					}
+				} else if ( handle.name === 'END' ) {
 
-				}
+					handle.position.copy( this.worldPosition );
+					handle.visible = this.dragging;
 
-			} else if ( _mode === "scale" ) {
+				} else if ( handle.name === 'DELTA' ) {
 
-				point.sub( offset );
-				point.multiply( parentScale );
+					handle.position.copy( this.worldPositionStart );
+					handle.quaternion.copy( this.worldQuaternionStart );
+					tempVector.set( 1e-10, 1e-10, 1e-10 ).add( this.worldPositionStart ).sub( this.worldPosition ).multiplyScalar( -1 );
+					tempVector.applyQuaternion( this.worldQuaternionStart.clone().inverse() );
+					handle.scale.copy( tempVector );
+					handle.visible = this.dragging;
 
-				if ( scope.space === "local" ) {
+				} else {
 
-					if ( scope.axis === "XYZ" ) {
+					handle.quaternion.copy( quaternion );
 
-						scale = 1 + ( ( point.y ) / Math.max( oldScale.x, oldScale.y, oldScale.z ) );
+					if ( this.dragging ) {
 
-						scope.object.scale.x = oldScale.x * scale;
-						scope.object.scale.y = oldScale.y * scale;
-						scope.object.scale.z = oldScale.z * scale;
+						handle.position.copy( this.worldPositionStart );
 
 					} else {
 
-						point.applyMatrix4( tempMatrix.getInverse( worldRotationMatrix ) );
+						handle.position.copy( this.worldPosition );
+
+					}
+
+					if ( this.axis ) {
 
-						if ( scope.axis === "X" ) scope.object.scale.x = oldScale.x * ( 1 + point.x / oldScale.x );
-						if ( scope.axis === "Y" ) scope.object.scale.y = oldScale.y * ( 1 + point.y / oldScale.y );
-						if ( scope.axis === "Z" ) scope.object.scale.z = oldScale.z * ( 1 + point.z / oldScale.z );
+						handle.visible = this.axis.search( handle.name ) !== -1;
 
 					}
 
 				}
 
-			} else if ( _mode === "rotate" ) {
-
-				point.sub( worldPosition );
-				point.multiply( parentScale );
-				tempVector.copy( offset ).sub( worldPosition );
-				tempVector.multiply( parentScale );
+				// If updating helper, skip rest of the loop
+				continue;
 
-				if ( scope.axis === "E" ) {
+			}
 
-					point.applyMatrix4( tempMatrix.getInverse( lookAtMatrix ) );
-					tempVector.applyMatrix4( tempMatrix.getInverse( lookAtMatrix ) );
+			// Align handles to current local or world rotation
 
-					rotation.set( Math.atan2( point.z, point.y ), Math.atan2( point.x, point.z ), Math.atan2( point.y, point.x ) );
-					offsetRotation.set( Math.atan2( tempVector.z, tempVector.y ), Math.atan2( tempVector.x, tempVector.z ), Math.atan2( tempVector.y, tempVector.x ) );
+			handle.quaternion.copy( quaternion );
 
-					tempQuaternion.setFromRotationMatrix( tempMatrix.getInverse( parentRotationMatrix ) );
+			if ( this.mode === 'translate' || this.mode === 'scale' ) {
 
-					quaternionE.setFromAxisAngle( eye, rotation.z - offsetRotation.z );
-					quaternionXYZ.setFromRotationMatrix( worldRotationMatrix );
+				// Hide translate and scale axis facing the camera
 
-					tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionE );
-					tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionXYZ );
+				var AXIS_HIDE_TRESHOLD = 0.99;
+				var PLANE_HIDE_TRESHOLD = 0.2;
+				var AXIS_FLIP_TRESHOLD = -0.4;
 
-					scope.object.quaternion.copy( tempQuaternion );
+				if ( handle.name === 'X' || handle.name === 'XYZX' ) {
+					if ( Math.abs( alignVector.copy( unitX ).applyQuaternion( quaternion ).dot( this.eye ) ) > AXIS_HIDE_TRESHOLD ) {
+						handle.scale.set( 1e-10, 1e-10, 1e-10 );
+						handle.visible = false;
+					}
+				}
+				if ( handle.name === 'Y' || handle.name === 'XYZY' ) {
+					if ( Math.abs( alignVector.copy( unitY ).applyQuaternion( quaternion ).dot( this.eye ) ) > AXIS_HIDE_TRESHOLD ) {
+						handle.scale.set( 1e-10, 1e-10, 1e-10 );
+						handle.visible = false;
+					}
+				}
+				if ( handle.name === 'Z' || handle.name === 'XYZZ' ) {
+					if ( Math.abs( alignVector.copy( unitZ ).applyQuaternion( quaternion ).dot( this.eye ) ) > AXIS_HIDE_TRESHOLD ) {
+						handle.scale.set( 1e-10, 1e-10, 1e-10 );
+						handle.visible = false;
+					}
+				}
+				if ( handle.name === 'XY' ) {
+					if ( Math.abs( alignVector.copy( unitZ ).applyQuaternion( quaternion ).dot( this.eye ) ) < PLANE_HIDE_TRESHOLD ) {
+						handle.scale.set( 1e-10, 1e-10, 1e-10 );
+						handle.visible = false;
+					}
+				}
+				if ( handle.name === 'YZ' ) {
+					if ( Math.abs( alignVector.copy( unitX ).applyQuaternion( quaternion ).dot( this.eye ) ) < PLANE_HIDE_TRESHOLD ) {
+						handle.scale.set( 1e-10, 1e-10, 1e-10 );
+						handle.visible = false;
+					}
+				}
+				if ( handle.name === 'XZ' ) {
+					if ( Math.abs( alignVector.copy( unitY ).applyQuaternion( quaternion ).dot( this.eye ) ) < PLANE_HIDE_TRESHOLD ) {
+						handle.scale.set( 1e-10, 1e-10, 1e-10 );
+						handle.visible = false;
+					}
+				}
 
-				} else if ( scope.axis === "XYZE" ) {
+				// Flip translate and scale axis ocluded behind another axis
+
+				if ( handle.name.search( 'X' ) !== -1 ) {
+					if ( alignVector.copy( unitX ).applyQuaternion( quaternion ).dot( this.eye ) < AXIS_FLIP_TRESHOLD ) {
+						if ( handle.tag === 'fwd' ) {
+							handle.visible = false;
+						} else {
+							handle.scale.x *= -1;
+						}
+					} else if ( handle.tag === 'bwd' ) {
+						handle.visible = false;
+					}
+				}
 
-					quaternionE.setFromEuler( point.clone().cross( tempVector ).normalize() ); // rotation axis
+				if ( handle.name.search( 'Y' ) !== -1 ) {
+					if ( alignVector.copy( unitY ).applyQuaternion( quaternion ).dot( this.eye ) < AXIS_FLIP_TRESHOLD ) {
+						if ( handle.tag === 'fwd' ) {
+							handle.visible = false;
+						} else {
+							handle.scale.y *= -1;
+						}
+					} else if ( handle.tag === 'bwd' ) {
+						handle.visible = false;
+					}
+				}
 
-					tempQuaternion.setFromRotationMatrix( tempMatrix.getInverse( parentRotationMatrix ) );
-					quaternionX.setFromAxisAngle( quaternionE, - point.clone().angleTo( tempVector ) );
-					quaternionXYZ.setFromRotationMatrix( worldRotationMatrix );
+				if ( handle.name.search( 'Z' ) !== -1 ) {
+					if ( alignVector.copy( unitZ ).applyQuaternion( quaternion ).dot( this.eye ) < AXIS_FLIP_TRESHOLD ) {
+						if ( handle.tag === 'fwd' ) {
+							handle.visible = false;
+						} else {
+							handle.scale.z *= -1;
+						}
+					} else if ( handle.tag === 'bwd' ) {
+						handle.visible = false;
+					}
+				}
 
-					tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionX );
-					tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionXYZ );
+			} else if ( this.mode === 'rotate' ) {
 
-					scope.object.quaternion.copy( tempQuaternion );
+				// Align handles to current local or world rotation
 
-				} else if ( scope.space === "local" ) {
+				tempQuaternion2.copy( quaternion );
+				alignVector.copy( this.eye ).applyQuaternion( tempQuaternion.copy( quaternion ).inverse() );
 
-					point.applyMatrix4( tempMatrix.getInverse( worldRotationMatrix ) );
+				if ( handle.name.search( "E" ) !== - 1 ) {
 
-					tempVector.applyMatrix4( tempMatrix.getInverse( worldRotationMatrix ) );
+					handle.quaternion.setFromRotationMatrix( lookAtMatrix.lookAt( this.eye, zeroVector, unitY ) );
 
-					rotation.set( Math.atan2( point.z, point.y ), Math.atan2( point.x, point.z ), Math.atan2( point.y, point.x ) );
-					offsetRotation.set( Math.atan2( tempVector.z, tempVector.y ), Math.atan2( tempVector.x, tempVector.z ), Math.atan2( tempVector.y, tempVector.x ) );
+				}
 
-					quaternionXYZ.setFromRotationMatrix( oldRotationMatrix );
+				if ( handle.name === 'X' ) {
 
-					if ( scope.rotationSnap !== null ) {
+					tempQuaternion.setFromAxisAngle( unitX, Math.atan2( -alignVector.y, alignVector.z ) );
+					tempQuaternion.multiplyQuaternions( tempQuaternion2, tempQuaternion );
+					handle.quaternion.copy( tempQuaternion );
 
-						quaternionX.setFromAxisAngle( unitX, Math.round( ( rotation.x - offsetRotation.x ) / scope.rotationSnap ) * scope.rotationSnap );
-						quaternionY.setFromAxisAngle( unitY, Math.round( ( rotation.y - offsetRotation.y ) / scope.rotationSnap ) * scope.rotationSnap );
-						quaternionZ.setFromAxisAngle( unitZ, Math.round( ( rotation.z - offsetRotation.z ) / scope.rotationSnap ) * scope.rotationSnap );
+				}
 
-					} else {
+				if ( handle.name === 'Y' ) {
 
-						quaternionX.setFromAxisAngle( unitX, rotation.x - offsetRotation.x );
-						quaternionY.setFromAxisAngle( unitY, rotation.y - offsetRotation.y );
-						quaternionZ.setFromAxisAngle( unitZ, rotation.z - offsetRotation.z );
+					tempQuaternion.setFromAxisAngle( unitY, Math.atan2( alignVector.x, alignVector.z ) );
+					tempQuaternion.multiplyQuaternions( tempQuaternion2, tempQuaternion );
+					handle.quaternion.copy( tempQuaternion );
 
-					}
+				}
 
-					if ( scope.axis === "X" ) quaternionXYZ.multiplyQuaternions( quaternionXYZ, quaternionX );
-					if ( scope.axis === "Y" ) quaternionXYZ.multiplyQuaternions( quaternionXYZ, quaternionY );
-					if ( scope.axis === "Z" ) quaternionXYZ.multiplyQuaternions( quaternionXYZ, quaternionZ );
+				if ( handle.name === 'Z' ) {
 
-					scope.object.quaternion.copy( quaternionXYZ );
+					tempQuaternion.setFromAxisAngle( unitZ, Math.atan2( alignVector.y, alignVector.x ) );
+					tempQuaternion.multiplyQuaternions( tempQuaternion2, tempQuaternion );
+					handle.quaternion.copy( tempQuaternion );
 
-				} else if ( scope.space === "world" ) {
+				}
 
-					rotation.set( Math.atan2( point.z, point.y ), Math.atan2( point.x, point.z ), Math.atan2( point.y, point.x ) );
-					offsetRotation.set( Math.atan2( tempVector.z, tempVector.y ), Math.atan2( tempVector.x, tempVector.z ), Math.atan2( tempVector.y, tempVector.x ) );
+			}
 
-					tempQuaternion.setFromRotationMatrix( tempMatrix.getInverse( parentRotationMatrix ) );
+			// highlight selected axis
 
-					if ( scope.rotationSnap !== null ) {
+			handle.material._opacity = handle.material._opacity || handle.material.opacity;
+			handle.material._color = handle.material._color || handle.material.color.clone();
 
-						quaternionX.setFromAxisAngle( unitX, Math.round( ( rotation.x - offsetRotation.x ) / scope.rotationSnap ) * scope.rotationSnap );
-						quaternionY.setFromAxisAngle( unitY, Math.round( ( rotation.y - offsetRotation.y ) / scope.rotationSnap ) * scope.rotationSnap );
-						quaternionZ.setFromAxisAngle( unitZ, Math.round( ( rotation.z - offsetRotation.z ) / scope.rotationSnap ) * scope.rotationSnap );
+			handle.material.color.copy( handle.material._color );
+			handle.material.opacity = handle.material._opacity;
 
-					} else {
+			if ( this.axis ) {
 
-						quaternionX.setFromAxisAngle( unitX, rotation.x - offsetRotation.x );
-						quaternionY.setFromAxisAngle( unitY, rotation.y - offsetRotation.y );
-						quaternionZ.setFromAxisAngle( unitZ, rotation.z - offsetRotation.z );
+				if ( handle.name === this.axis ) {
 
-					}
+					handle.material.opacity = 1.0;
+					handle.material.color.lerp( new THREE.Color( 1, 1, 1 ), 0.5 );
 
-					quaternionXYZ.setFromRotationMatrix( worldRotationMatrix );
+				} else if ( this.axis.split('').some( function( a ) { return handle.name === a; } ) ) {
 
-					if ( scope.axis === "X" ) tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionX );
-					if ( scope.axis === "Y" ) tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionY );
-					if ( scope.axis === "Z" ) tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionZ );
+					handle.material.opacity = 1.0;
+					handle.material.color.lerp( new THREE.Color( 1, 1, 1 ), 0.5 );
 
-					tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionXYZ );
+				} else {
 
-					scope.object.quaternion.copy( tempQuaternion );
+					handle.material.opacity *= 0.05;
 
 				}
 
 			}
 
-			scope.update();
-			scope.dispatchEvent( changeEvent );
-			scope.dispatchEvent( objectChangeEvent );
-
 		}
 
-		function onPointerUp( event ) {
-
-			event.preventDefault(); // Prevent MouseEvent on mobile
-
-			if ( event.button !== undefined && event.button !== 0 ) return;
+		THREE.Object3D.prototype.updateMatrixWorld.call( this );
 
-			if ( _dragging && ( scope.axis !== null ) ) {
-
-				mouseUpEvent.mode = _mode;
-				scope.dispatchEvent( mouseUpEvent );
+	};
 
-			}
+};
 
-			_dragging = false;
+THREE.TransformControlsGizmo.prototype = Object.assign( Object.create( THREE.Object3D.prototype ), {
 
-			if ( 'TouchEvent' in window && event instanceof TouchEvent ) {
+	constructor: THREE.TransformControlsGizmo,
 
-				// Force "rollover"
+	isTransformControlsGizmo: true
 
-				scope.axis = null;
-				scope.update();
-				scope.dispatchEvent( changeEvent );
+} );
 
-			} else {
 
-				onPointerHover( event );
+THREE.TransformControlsPlane = function () {
 
-			}
+	'use strict';
 
+	THREE.Mesh.call( this,
+		new THREE.PlaneBufferGeometry( 100000, 100000, 2, 2 ),
+		new THREE.MeshBasicMaterial( { visible: false, wireframe: true, side: THREE.DoubleSide, transparent: true, opacity: 0.1 } )
+	);
+
+	this.type = 'TransformControlsPlane';
+
+	var unitX = new THREE.Vector3( 1, 0, 0 );
+	var unitY = new THREE.Vector3( 0, 1, 0 );
+	var unitZ = new THREE.Vector3( 0, 0, 1 );
+
+	var tempVector = new THREE.Vector3();
+	var dirVector = new THREE.Vector3();
+	var alignVector = new THREE.Vector3();
+	var tempMatrix = new THREE.Matrix4();
+	var camRotation = new THREE.Euler();
+	var identityQuaternion = new THREE.Quaternion();
+
+	this.updateMatrixWorld = function() {
+
+		var space = this.space;
+
+		this.position.copy( this.worldPosition );
+
+		if ( this.mode === 'scale' ) space = 'local'; // scale always oriented to local rotation
+
+		unitX.set( 1, 0, 0 ).applyQuaternion( space === "local" ? this.worldQuaternion : identityQuaternion );
+		unitY.set( 0, 1, 0 ).applyQuaternion( space === "local" ? this.worldQuaternion : identityQuaternion );
+		unitZ.set( 0, 0, 1 ).applyQuaternion( space === "local" ? this.worldQuaternion : identityQuaternion );
+
+		// Align the plane for current transform mode, axis and space.
+
+		alignVector.copy( unitY );
+
+		switch ( this.mode ) {
+			case 'translate':
+			case 'scale':
+				switch ( this.axis ) {
+					case 'X':
+						alignVector.copy( this.eye ).cross( unitX );
+						dirVector.copy( unitX ).cross( alignVector );
+						break;
+					case 'Y':
+						alignVector.copy( this.eye ).cross( unitY );
+						dirVector.copy( unitY ).cross( alignVector );
+						break;
+					case 'Z':
+						alignVector.copy( this.eye ).cross( unitZ );
+						dirVector.copy( unitZ ).cross( alignVector );
+						break;
+					case 'XY':
+						dirVector.copy( unitZ );
+						break;
+					case 'YZ':
+						dirVector.copy( unitX );
+						break;
+					case 'XZ':
+						alignVector.copy( unitZ );
+						dirVector.copy( unitY );
+						break;
+					case 'XYZ':
+					case 'E':
+						dirVector.set( 0, 0, 0 );
+						break;
+				}
+				break;
+			case 'rotate':
+			default:
+				// special case for rotate
+				dirVector.set( 0, 0, 0 );
 		}
 
-		function intersectObjects( pointer, objects ) {
+		if ( dirVector.length() === 0 ) {
+
+			// If in rotate mode, make the plane parallel to camera
+			this.quaternion.copy( this.cameraQuaternion );
 
-			var rect = domElement.getBoundingClientRect();
-			var x = ( pointer.clientX - rect.left ) / rect.width;
-			var y = ( pointer.clientY - rect.top ) / rect.height;
+		} else {
 
-			pointerVector.set( ( x * 2 ) - 1, - ( y * 2 ) + 1 );
-			ray.setFromCamera( pointerVector, camera );
+			tempMatrix.lookAt( tempVector.set( 0, 0, 0 ), dirVector, alignVector );
 
-			var intersections = ray.intersectObjects( objects, true );
-			return intersections[ 0 ] ? intersections[ 0 ] : false;
+			this.quaternion.setFromRotationMatrix( tempMatrix );
 
 		}
 
+		THREE.Object3D.prototype.updateMatrixWorld.call( this );
+
 	};
 
-	THREE.TransformControls.prototype = Object.create( THREE.Object3D.prototype );
-	THREE.TransformControls.prototype.constructor = THREE.TransformControls;
+};
+
+THREE.TransformControlsPlane.prototype = Object.assign( Object.create( THREE.Mesh.prototype ), {
+
+	constructor: THREE.TransformControlsPlane,
+
+	isTransformControlsPlane: true
 
-}() );
+} );

+ 596 - 0
examples/js/exporters/ColladaExporter.js

@@ -0,0 +1,596 @@
+/**
+ * @author Garrett Johnson / http://gkjohnson.github.io/
+ * https://github.com/gkjohnson/collada-exporter-js
+ *
+ * Usage:
+ *  var exporter = new THREE.ColladaExporter();
+ *
+ *  var data = exporter.parse(mesh);
+ *
+ * Format Definition:
+ *  https://www.khronos.org/collada/
+ */
+
+THREE.ColladaExporter = function () {};
+
+THREE.ColladaExporter.prototype = {
+
+	constructor: THREE.ColladaExporter,
+
+	parse: function ( object, onDone, options = {} ) {
+
+		options = Object.assign( {
+			version: '1.4.1',
+			author: null,
+			textureDirectory: '',
+		}, options );
+
+		if ( options.textureDirectory !== '' ) {
+
+			options.textureDirectory = `${ options.textureDirectory }/`
+				.replace( /\\/g, '/' )
+				.replace( /\/+/g, '/' );
+
+		}
+
+		var version = options.version;
+		if ( version !== '1.4.1' && version !== '1.5.0' ) {
+
+			console.warn( `ColladaExporter : Version ${ version } not supported for export. Only 1.4.1 and 1.5.0.` );
+			return null;
+
+		}
+
+		// Convert the urdf xml into a well-formatted, indented format
+		function format( urdf ) {
+
+			var IS_END_TAG = /^<\//;
+			var IS_SELF_CLOSING = /(\?>$)|(\/>$)/;
+			var HAS_TEXT = /<[^>]+>[^<]*<\/[^<]+>/;
+
+			var pad = ( ch, num ) => ( num > 0 ? ch + pad( ch, num - 1 ) : '' );
+
+			var tagnum = 0;
+			return urdf
+				.match( /(<[^>]+>[^<]+<\/[^<]+>)|(<[^>]+>)/g )
+				.map( tag => {
+
+					if ( ! HAS_TEXT.test( tag ) && ! IS_SELF_CLOSING.test( tag ) && IS_END_TAG.test( tag ) ) {
+
+						tagnum --;
+
+					}
+
+					var res = `${ pad( '  ', tagnum ) }${ tag }`;
+
+					if ( ! HAS_TEXT.test( tag ) && ! IS_SELF_CLOSING.test( tag ) && ! IS_END_TAG.test( tag ) ) {
+
+						tagnum ++;
+
+					}
+
+					return res;
+
+				} )
+				.join( '\n' );
+
+		}
+
+		// Convert an image into a png format for saving
+		function base64ToBuffer( str ) {
+
+			var b = atob( str );
+			var buf = new Uint8Array( b.length );
+
+			for ( var i = 0, l = buf.length; i < l; i ++ ) {
+
+				buf[ i ] = b.charCodeAt( i );
+
+			}
+
+			return buf;
+
+		}
+
+		var canvas, ctx;
+		function imageToData( image, ext ) {
+
+			canvas = canvas || document.createElement( 'canvas' );
+			ctx = ctx || canvas.getContext( '2d' );
+
+			canvas.width = image.naturalWidth;
+			canvas.height = image.naturalHeight;
+
+			ctx.drawImage( image, 0, 0 );
+
+			// Get the base64 encoded data
+			var base64data = canvas
+				.toDataURL( `image/${ ext }`, 1 )
+				.replace( /^data:image\/(png|jpg);base64,/, '' );
+
+			// Convert to a uint8 array
+			return base64ToBuffer( base64data );
+
+		}
+
+		// gets the attribute array. Generate a new array if the attribute is interleaved
+		var getFuncs = [ 'getX', 'getY', 'getZ', 'getW' ];
+		function attrBufferToArray( attr ) {
+
+			if ( attr.isInterleavedBufferAttribute ) {
+
+				// use the typed array constructor to save on memory
+				var arr = new attr.array.constructor( attr.count * attr.itemSize );
+				var size = attr.itemSize;
+				for ( var i = 0, l = attr.count; i < l; i ++ ) {
+
+					for ( var j = 0; j < size; j ++ ) {
+
+						arr[ i * size + j ] = attr[ getFuncs[ j ] ]( i );
+
+					}
+
+				}
+
+				return arr;
+
+			} else {
+
+				return attr.array;
+
+			}
+
+		}
+
+		// Returns an array of the same type starting at the `st` index,
+		// and `ct` length
+		function subArray( arr, st, ct ) {
+
+			if ( Array.isArray( arr ) ) return arr.slice( st, st + ct );
+			else return new arr.constructor( arr.buffer, st * arr.BYTES_PER_ELEMENT, ct );
+
+		}
+
+		// Returns the string for a geometry's attribute
+		function getAttribute( attr, name, params, type ) {
+
+			var array = attrBufferToArray( attr );
+			var res =
+					`<source id="${ name }">` +
+
+					`<float_array id="${ name }-array" count="${ array.length }">` +
+					array.join( ' ' ) +
+					'</float_array>' +
+
+					'<technique_common>' +
+					`<accessor source="#${ name }-array" count="${ Math.floor( array.length / attr.itemSize ) }" stride="${ attr.itemSize }">` +
+
+					params.map( n => `<param name="${ n }" type="${ type }" />` ).join( '' ) +
+
+					'</accessor>' +
+					'</technique_common>' +
+					'</source>';
+
+			return res;
+
+		}
+
+		// Returns the string for a node's transform information
+		var transMat;
+		function getTransform( o ) {
+
+			// ensure the object's matrix is up to date
+			// before saving the transform
+			o.updateMatrix();
+
+			transMat = transMat || new THREE.Matrix4();
+			transMat.copy( o.matrix );
+			transMat.transpose();
+			return `<matrix>${ transMat.toArray().join( ' ' ) }</matrix>`;
+
+		}
+
+		// Process the given piece of geometry into the geometry library
+		// Returns the mesh id
+		function processGeometry( g ) {
+
+			var meshid = geometryMap.get( g );
+
+			// convert the geometry to bufferGeometry if it isn't already
+			var processGeom = g;
+			if ( processGeom instanceof THREE.Geometry ) {
+
+				processGeom = ( new THREE.BufferGeometry() ).fromGeometry( processGeom );
+
+			}
+
+			if ( meshid == null ) {
+
+				meshid = `Mesh${ libraryGeometries.length + 1 }`;
+
+				var indexCount =
+					processGeom.index ?
+						processGeom.index.count * processGeom.index.itemSize :
+						processGeom.attributes.position.count;
+
+				var groups =
+					processGeom.groups != null && processGeom.groups.length !== 0 ?
+						processGeom.groups :
+						[ { start: 0, count: indexCount, materialIndex: 0 } ];
+
+				var gnode = `<geometry id="${ meshid }" name="${ g.name }"><mesh>`;
+
+				// define the geometry node and the vertices for the geometry
+				var posName = `${ meshid }-position`;
+				var vertName = `${ meshid }-vertices`;
+				gnode += getAttribute( processGeom.attributes.position, posName, [ 'X', 'Y', 'Z' ], 'float' );
+				gnode += `<vertices id="${ vertName }"><input semantic="POSITION" source="#${ posName }" /></vertices>`;
+
+				// NOTE: We're not optimizing the attribute arrays here, so they're all the same length and
+				// can therefore share the same triangle indices. However, MeshLab seems to have trouble opening
+				// models with attributes that share an offset.
+				// MeshLab Bug#424: https://sourceforge.net/p/meshlab/bugs/424/
+
+				// serialize normals
+				var triangleInputs = `<input semantic="VERTEX" source="#${ vertName }" offset="0" />`;
+				if ( 'normal' in processGeom.attributes ) {
+
+					var normName = `${ meshid }-normal`;
+					gnode += getAttribute( processGeom.attributes.normal, normName, [ 'X', 'Y', 'Z' ], 'float' );
+					triangleInputs += `<input semantic="NORMAL" source="#${ normName }" offset="0" />`;
+
+				}
+
+				// serialize uvs
+				if ( 'uv' in processGeom.attributes ) {
+
+					var uvName = `${ meshid }-texcoord`;
+					gnode += getAttribute( processGeom.attributes.uv, uvName, [ 'S', 'T' ], 'float' );
+					triangleInputs += `<input semantic="TEXCOORD" source="#${ uvName }" offset="0" set="0" />`;
+
+				}
+
+				// serialize colors
+				if ( 'color' in processGeom.attributes ) {
+
+					var colName = `${ meshid }-color`;
+					gnode += getAttribute( processGeom.attributes.color, colName, [ 'X', 'Y', 'Z' ], 'uint8' );
+					triangleInputs += `<input semantic="COLOR" source="#${ colName }" offset="0" />`;
+
+				}
+
+				var indexArray = null;
+				if ( processGeom.index ) {
+
+					indexArray = attrBufferToArray( processGeom.index );
+
+				} else {
+
+					indexArray = new Array( indexCount );
+					for ( var i = 0, l = indexArray.length; i < l; i ++ ) indexArray[ i ] = i;
+
+				}
+
+				for ( var i = 0, l = groups.length; i < l; i ++ ) {
+
+					var group = groups[ i ];
+					var subarr = subArray( indexArray, group.start, group.count );
+					var polycount = subarr.length / 3;
+					gnode += `<triangles material="MESH_MATERIAL_${ group.materialIndex }" count="${ polycount }">`;
+					gnode += triangleInputs;
+
+					gnode += `<p>${ subarr.join( ' ' ) }</p>`;
+					gnode += '</triangles>';
+
+				}
+
+				gnode += `</mesh></geometry>`;
+
+				libraryGeometries.push( gnode );
+				geometryMap.set( g, meshid );
+
+			}
+
+			return meshid;
+
+		}
+
+		// Process the given texture into the image library
+		// Returns the image library
+		function processTexture( tex ) {
+
+			var texid = imageMap.get( tex );
+			if ( texid == null ) {
+
+				texid = `image-${ libraryImages.length + 1 }`;
+
+				var ext = 'png';
+				var name = tex.name || texid;
+				var imageNode = `<image id="${ texid }" name="${ name }">`;
+
+				if ( version === '1.5.0' ) {
+
+					imageNode += `<init_from><ref>${ options.textureDirectory }${ name }.${ ext }</ref></init_from>`;
+
+				} else {
+
+					// version image node 1.4.1
+					imageNode += `<init_from>${ options.textureDirectory }${ name }.${ ext }</init_from>`;
+
+				}
+
+				imageNode += '</image>';
+
+				libraryImages.push( imageNode );
+				imageMap.set( tex, texid );
+				textures.push( {
+					directory: options.textureDirectory,
+					name,
+					ext,
+					data: imageToData( tex.image, ext ),
+					original: tex
+				} );
+
+			}
+
+			return texid;
+
+		}
+
+		// Process the given material into the material and effect libraries
+		// Returns the material id
+		function processMaterial( m ) {
+
+			var matid = materialMap.get( m );
+
+			if ( matid == null ) {
+
+				matid = `Mat${ libraryEffects.length + 1 }`;
+
+				var type = 'phong';
+
+				if ( m instanceof THREE.MeshLambertMaterial ) {
+
+					type = 'lambert';
+
+				} else if ( m instanceof THREE.MeshBasicMaterial ) {
+
+					type = 'constant';
+
+				}
+
+				var emissive = m.emissive ? m.emissive : new THREE.Color( 0, 0, 0 );
+				var diffuse = m.color ? m.color : new THREE.Color( 0, 0, 0 );
+				var specular = m.specular ? m.specular : new THREE.Color( 1, 1, 1 );
+				var shininess = m.shininess || 0;
+				var reflectivity = m.reflectivity || 0;
+
+				// Do not export and alpha map for the reasons mentioned in issue (#13792)
+				// in THREE.js alpha maps are black and white, but collada expects the alpha
+				// channel to specify the transparency
+				var transparencyNode = '';
+				if ( m.transparent === true ) {
+
+					transparencyNode +=
+						`<transparent>` +
+						(
+							m.map ?
+								`<texture texture="diffuse-sampler"></texture>` :
+								'<float>1</float>'
+						) +
+						'</transparent>';
+
+					if ( m.opacity < 1 ) {
+
+						transparencyNode += `<transparency><float>${ m.opacity }</float></transparency>`;
+
+					}
+
+				}
+
+				var techniqueNode = `<technique sid="common"><${ type }>` +
+
+					'<emission>' +
+
+					(
+						m.emissiveMap ?
+							'<texture texture="emissive-sampler" texcoord="TEXCOORD" />' :
+							`<color sid="emission">${ emissive.r } ${ emissive.g } ${ emissive.b } 1</color>`
+					) +
+
+					'</emission>' +
+
+					'<diffuse>' +
+
+					(
+						m.map ?
+							'<texture texture="diffuse-sampler" texcoord="TEXCOORD" />' :
+							`<color sid="diffuse">${ diffuse.r } ${ diffuse.g } ${ diffuse.b } 1</color>`
+					) +
+
+					'</diffuse>' +
+
+					`<specular><color sid="specular">${ specular.r } ${ specular.g } ${ specular.b } 1</color></specular>` +
+
+					'<shininess>' +
+
+					(
+						m.specularMap ?
+							'<texture texture="specular-sampler" texcoord="TEXCOORD" />' :
+							`<float sid="shininess">${ shininess }</float>`
+					) +
+
+					'</shininess>' +
+
+					`<reflective><color>${ diffuse.r } ${ diffuse.g } ${ diffuse.b } 1</color></reflective>` +
+
+					`<reflectivity><float>${ reflectivity }</float></reflectivity>` +
+
+					transparencyNode +
+
+					`</${ type }></technique>`;
+
+				var effectnode =
+					`<effect id="${ matid }-effect">` +
+					'<profile_COMMON>' +
+
+					(
+						m.map ?
+							'<newparam sid="diffuse-surface"><surface type="2D">' +
+							`<init_from>${ processTexture( m.map ) }</init_from>` +
+							'</surface></newparam>' +
+							'<newparam sid="diffuse-sampler"><sampler2D><source>diffuse-surface</source></sampler2D></newparam>' :
+							''
+					) +
+
+					(
+						m.specularMap ?
+							'<newparam sid="specular-surface"><surface type="2D">' +
+							`<init_from>${ processTexture( m.specularMap ) }</init_from>` +
+							'</surface></newparam>' +
+							'<newparam sid="specular-sampler"><sampler2D><source>specular-surface</source></sampler2D></newparam>' :
+							''
+					) +
+
+					(
+						m.emissiveMap ?
+							'<newparam sid="emissive-surface"><surface type="2D">' +
+							`<init_from>${ processTexture( m.emissiveMap ) }</init_from>` +
+							'</surface></newparam>' +
+							'<newparam sid="emissive-sampler"><sampler2D><source>emissive-surface</source></sampler2D></newparam>' :
+							''
+					) +
+
+					techniqueNode +
+
+					(
+						m.side === THREE.DoubleSide ?
+							`<extra><technique><double_sided sid="double_sided" type="int">1</double_sided></technique></extra>` :
+							''
+					) +
+
+					'</profile_COMMON>' +
+
+					'</effect>';
+
+				libraryMaterials.push( `<material id="${ matid }" name="${ m.name }"><instance_effect url="#${ matid }-effect" /></material>` );
+				libraryEffects.push( effectnode );
+				materialMap.set( m, matid );
+
+			}
+
+			return matid;
+
+		}
+
+		// Recursively process the object into a scene
+		function processObject( o ) {
+
+			var node = `<node name="${ o.name }">`;
+
+			node += getTransform( o );
+
+			if ( o instanceof THREE.Mesh && o.geometry != null ) {
+
+				var meshid = processGeometry( o.geometry, meshid );
+
+				// ids of the materials to bind to the geometry
+				var matids = null;
+
+				// get a list of materials to bind to the sub groups of the geometry.
+				// If the amount of subgroups is greater than the materials, than reuse
+				// the materials.
+				var mat = o.material || new THREE.MeshBasicMaterial();
+				var materials = Array.isArray( mat ) ? mat : [ mat ];
+				matids = new Array( o.geometry.groups.length )
+					.fill()
+					.map( ( v, i ) => processMaterial( materials[ i % materials.length ] ) );
+
+
+				node +=
+					`<instance_geometry url="#${ meshid }">` +
+
+					(
+						matids != null ?
+							'<bind_material><technique_common>' +
+							matids.map( ( id, i ) =>
+
+								`<instance_material symbol="MESH_MATERIAL_${ i }" target="#${ id }" >` +
+
+								'<bind_vertex_input semantic="TEXCOORD" input_semantic="TEXCOORD" input_set="0" />' +
+
+								'</instance_material>'
+							).join( '' ) +
+							'</technique_common></bind_material>' :
+							''
+					) +
+
+					'</instance_geometry>';
+
+			}
+
+			o.children.forEach( c => node += processObject( c ) );
+
+			node += '</node>';
+
+			return node;
+
+		}
+
+		var geometryMap = new WeakMap();
+		var materialMap = new WeakMap();
+		var imageMap = new WeakMap();
+		var textures = [];
+
+		var libraryImages = [];
+		var libraryGeometries = [];
+		var libraryEffects = [];
+		var libraryMaterials = [];
+		var libraryVisualScenes = processObject( object );
+
+		var specLink = version === '1.4.1' ? 'http://www.collada.org/2005/11/COLLADASchema' : 'https://www.khronos.org/collada/';
+		var dae =
+			'<?xml version="1.0" encoding="UTF-8" standalone="no" ?>' +
+			`<COLLADA xmlns="${ specLink }" version="${ version }">` +
+			'<asset>' +
+			(
+				'<contributor>' +
+				'<authoring_tool>THREE.js Collada Exporter</authoring_tool>' +
+				( options.author !== null ? `<author>${ options.author }</author>` : '' ) +
+				'</contributor>' +
+				`<created>${ ( new Date() ).toISOString() }</created>` +
+				`<modified>${ ( new Date() ).toISOString() }</modified>` +
+				'<up_axis>Y_UP</up_axis>'
+			) +
+			'</asset>';
+
+		dae += `<library_images>${ libraryImages.join( '' ) }</library_images>`;
+
+		dae += `<library_effects>${ libraryEffects.join( '' ) }</library_effects>`;
+
+		dae += `<library_materials>${ libraryMaterials.join( '' ) }</library_materials>`;
+
+		dae += `<library_geometries>${ libraryGeometries.join( '' ) }</library_geometries>`;
+
+		dae += `<library_visual_scenes><visual_scene id="Scene" name="scene">${ libraryVisualScenes }</visual_scene></library_visual_scenes>`;
+
+		dae += '<scene><instance_visual_scene url="#Scene"/></scene>';
+
+		dae += '</COLLADA>';
+
+		var res = {
+			data: format( dae ),
+			textures
+		};
+
+		if ( typeof onDone === 'function' ) {
+
+			requestAnimationFrame( () => onDone( res ) );
+
+		}
+
+		return res;
+
+	}
+
+};

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

@@ -1397,6 +1397,16 @@ THREE.GLTFExporter.prototype = {
 
 				if ( trackProperty === PATH_PROPERTIES.morphTargetInfluences ) {
 
+					if ( trackNode.morphTargetInfluences.length !== 1 &&
+						trackBinding.propertyIndex !== undefined ) {
+
+						console.warn( 'THREE.GLTFExporter: Skipping animation track "%s". ' +
+							'Morph target keyframe tracks must target all available morph targets ' +
+							'for the given mesh.', track.name );
+						continue;
+
+					}
+
 					outputItemSize /= trackNode.morphTargetInfluences.length;
 
 				}

+ 17 - 5
examples/js/exporters/PLYExporter.js

@@ -6,10 +6,10 @@
  *  var exporter = new THREE.PLYExporter();
  *
  *  // second argument is a list of options
- *  var data = exporter.parse( mesh, { binary: true, excludeAttributes: [ 'color' ] } );
+ *  exporter.parse(mesh, data => console.log(data), { binary: true, excludeAttributes: [ 'color' ] });
  *
  * Format Definition:
- *  http://paulbourke.net/dataformats/ply/
+ * http://paulbourke.net/dataformats/ply/
  */
 
 THREE.PLYExporter = function () {};
@@ -18,7 +18,15 @@ THREE.PLYExporter.prototype = {
 
 	constructor: THREE.PLYExporter,
 
-	parse: function ( object, options ) {
+	parse: function ( object, onDone, options ) {
+
+		if ( onDone && typeof onDone === 'object' ) {
+
+			console.warn( 'THREE.PLYExporter: The options parameter is now the third argument to the "parse" function. See the documentation for the new API.' );
+			options = onDone;
+			onDone = undefined;
+
+		}
 
 		// Iterate over the valid meshes in the object
 		function traverseMeshes( cb ) {
@@ -208,6 +216,7 @@ THREE.PLYExporter.prototype = {
 		// Generate attribute data
 		var vertex = new THREE.Vector3();
 		var normalMatrixWorld = new THREE.Matrix3();
+		var result = null;
 
 		if ( options.binary === true ) {
 
@@ -399,7 +408,7 @@ THREE.PLYExporter.prototype = {
 
 			} );
 
-			return output;
+			result = output.buffer;
 
 		} else {
 
@@ -529,10 +538,13 @@ THREE.PLYExporter.prototype = {
 
 			} );
 
-			return `${ header }${vertexList}\n${ includeIndices ? `${faceList}\n` : '' }`;
+			result = `${ header }${vertexList}\n${ includeIndices ? `${faceList}\n` : '' }`;
 
 		}
 
+		if ( typeof onDone === 'function' ) requestAnimationFrame( () => onDone( result ) );
+		return result;
+
 	}
 
 };

+ 79 - 16
examples/js/loaders/ColladaLoader.js

@@ -969,11 +969,11 @@ THREE.ColladaLoader.prototype = {
 
 			if ( data.bindShapeMatrix ) {
 
-				build.bindMatrix = new THREE.Matrix4().fromArray( data.bindShapeMatrix ).transpose(); 
+				build.bindMatrix = new THREE.Matrix4().fromArray( data.bindShapeMatrix ).transpose();
 
 			} else {
 
-				build.bindMatrix = new THREE.Matrix4().transpose(); 
+				build.bindMatrix = new THREE.Matrix4().identity();
 
 			}
 
@@ -1449,6 +1449,28 @@ THREE.ColladaLoader.prototype = {
 
 		}
 
+		function getTextureLoader( image ) {
+
+			var loader;
+
+			var extension = image.slice( ( image.lastIndexOf( '.' ) - 1 >>> 0 ) + 2 ); // http://www.jstips.co/en/javascript/get-file-extension/
+			extension = extension.toLowerCase();
+
+			switch ( extension ) {
+
+				case 'tga':
+					loader = tgaLoader;
+					break;
+
+				default:
+					loader = textureLoader;
+
+			}
+
+			return loader;
+
+		}
+
 		function buildMaterial( data ) {
 
 			var effect = getEffect( data.url );
@@ -1499,28 +1521,40 @@ THREE.ColladaLoader.prototype = {
 
 				if ( image !== null ) {
 
-					var texture = textureLoader.load( image );
+					var loader = getTextureLoader( image );
+
+					if ( loader !== undefined ) {
+
+						var texture = loader.load( image );
+
+						var extra = textureObject.extra;
 
-					var extra = textureObject.extra;
+						if ( extra !== undefined && extra.technique !== undefined && isEmpty( extra.technique ) === false ) {
 
-					if ( extra !== undefined && extra.technique !== undefined && isEmpty( extra.technique ) === false ) {
+							var technique = extra.technique;
 
-						var technique = extra.technique;
+							texture.wrapS = technique.wrapU ? THREE.RepeatWrapping : THREE.ClampToEdgeWrapping;
+							texture.wrapT = technique.wrapV ? THREE.RepeatWrapping : THREE.ClampToEdgeWrapping;
 
-						texture.wrapS = technique.wrapU ? THREE.RepeatWrapping : THREE.ClampToEdgeWrapping;
-						texture.wrapT = technique.wrapV ? THREE.RepeatWrapping : THREE.ClampToEdgeWrapping;
+							texture.offset.set( technique.offsetU || 0, technique.offsetV || 0 );
+							texture.repeat.set( technique.repeatU || 1, technique.repeatV || 1 );
+
+						} else {
+
+							texture.wrapS = THREE.RepeatWrapping;
+							texture.wrapT = THREE.RepeatWrapping;
+
+						}
 
-						texture.offset.set( technique.offsetU || 0, technique.offsetV || 0 );
-						texture.repeat.set( technique.repeatU || 1, technique.repeatV || 1 );
+						return texture;
 
 					} else {
 
-						texture.wrapS = THREE.RepeatWrapping;
-						texture.wrapT = THREE.RepeatWrapping;
+						console.warn( 'THREE.ColladaLoader: Loader for texture %s not found.', image );
 
-					}
+						return null;
 
-					return texture;
+					}
 
 				} else {
 
@@ -3183,7 +3217,15 @@ THREE.ColladaLoader.prototype = {
 
 			}
 
-			library.nodes[ data.id ] = data;
+			if ( hasNode( data.id ) ) {
+
+				console.warn( 'THREE.ColladaLoader: There is already a node with ID %s. Exclude current node from further processing.', data.id );
+
+			} else {
+
+				library.nodes[ data.id ] = data;
+
+			}
 
 			return data;
 
@@ -3519,6 +3561,8 @@ THREE.ColladaLoader.prototype = {
 
 		}
 
+		var fallbackMaterial = new THREE.MeshBasicMaterial( { color: 0xff00ff } );
+
 		function resolveMaterialBinding( keys, instanceMaterials ) {
 
 			var materials = [];
@@ -3526,7 +3570,17 @@ THREE.ColladaLoader.prototype = {
 			for ( var i = 0, l = keys.length; i < l; i ++ ) {
 
 				var id = instanceMaterials[ keys[ i ] ];
-				materials.push( getMaterial( id ) );
+
+				if ( id === undefined ) {
+
+					console.warn( 'THREE.ColladaLoader: Material with key %s not found. Apply fallback material.', keys[ i ] );
+					materials.push( fallbackMaterial );
+
+				} else {
+
+					materials.push( getMaterial( id ) );
+
+				}
 
 			}
 
@@ -3749,6 +3803,15 @@ THREE.ColladaLoader.prototype = {
 		var textureLoader = new THREE.TextureLoader( this.manager );
 		textureLoader.setPath( path ).setCrossOrigin( this.crossOrigin );
 
+		var tgaLoader;
+
+		if ( THREE.TGALoader ) {
+
+			tgaLoader = new THREE.TGALoader( this.manager );
+			tgaLoader.setPath( path );
+
+		}
+
 		//
 
 		var animations = [];

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


+ 69 - 104
examples/js/loaders/GLTFLoader.js

@@ -130,7 +130,7 @@ THREE.GLTFLoader = ( function () {
 
 					switch ( extensionName ) {
 
-						case EXTENSIONS.KHR_LIGHTS:
+						case EXTENSIONS.KHR_LIGHTS_PUNCTUAL:
 							extensions[ extensionName ] = new GLTFLightsExtension( json );
 							break;
 
@@ -237,7 +237,7 @@ THREE.GLTFLoader = ( function () {
 	var EXTENSIONS = {
 		KHR_BINARY_GLTF: 'KHR_binary_glTF',
 		KHR_DRACO_MESH_COMPRESSION: 'KHR_draco_mesh_compression',
-		KHR_LIGHTS: 'KHR_lights',
+		KHR_LIGHTS_PUNCTUAL: 'KHR_lights_punctual',
 		KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS: 'KHR_materials_pbrSpecularGlossiness',
 		KHR_MATERIALS_UNLIT: 'KHR_materials_unlit',
 		MSFT_TEXTURE_DDS: 'MSFT_texture_dds'
@@ -270,21 +270,24 @@ THREE.GLTFLoader = ( function () {
 	 */
 	function GLTFLightsExtension( json ) {
 
-		this.name = EXTENSIONS.KHR_LIGHTS;
+		this.name = EXTENSIONS.KHR_LIGHTS_PUNCTUAL;
 
-		this.lights = {};
+		this.lights = [];
 
-		var extension = ( json.extensions && json.extensions[ EXTENSIONS.KHR_LIGHTS ] ) || {};
-		var lights = extension.lights || {};
+		var extension = ( json.extensions && json.extensions[ EXTENSIONS.KHR_LIGHTS_PUNCTUAL ] ) || {};
+		var lightDefs = extension.lights || [];
 
-		for ( var lightId in lights ) {
+		for ( var i = 0; i < lightDefs.length; i ++ ) {
 
-			var light = lights[ lightId ];
+			var lightDef = lightDefs[ i ];
 			var lightNode;
 
-			var color = new THREE.Color().fromArray( light.color );
+			var color = new THREE.Color( 0xffffff );
+			if ( lightDef.color !== undefined ) color.fromArray( lightDef.color );
 
-			switch ( light.type ) {
+			var range = lightDef.range !== undefined ? lightDef.range : 0;
+
+			switch ( lightDef.type ) {
 
 				case 'directional':
 					lightNode = new THREE.DirectionalLight( color );
@@ -294,40 +297,34 @@ THREE.GLTFLoader = ( function () {
 
 				case 'point':
 					lightNode = new THREE.PointLight( color );
+					lightNode.distance = range;
 					break;
 
 				case 'spot':
 					lightNode = new THREE.SpotLight( color );
+					lightNode.distance = range;
 					// Handle spotlight properties.
-					light.spot = light.spot || {};
-					light.spot.innerConeAngle = light.spot.innerConeAngle !== undefined ? light.spot.innerConeAngle : 0;
-					light.spot.outerConeAngle = light.spot.outerConeAngle !== undefined ? light.spot.outerConeAngle : Math.PI / 4.0;
-					lightNode.angle = light.spot.outerConeAngle;
-					lightNode.penumbra = 1.0 - light.spot.innerConeAngle / light.spot.outerConeAngle;
+					lightDef.spot = lightDef.spot || {};
+					lightDef.spot.innerConeAngle = lightDef.spot.innerConeAngle !== undefined ? lightDef.spot.innerConeAngle : 0;
+					lightDef.spot.outerConeAngle = lightDef.spot.outerConeAngle !== undefined ? lightDef.spot.outerConeAngle : Math.PI / 4.0;
+					lightNode.angle = lightDef.spot.outerConeAngle;
+					lightNode.penumbra = 1.0 - lightDef.spot.innerConeAngle / lightDef.spot.outerConeAngle;
 					lightNode.target.position.set( 0, 0, 1 );
 					lightNode.add( lightNode.target );
 					break;
 
-				case 'ambient':
-					lightNode = new THREE.AmbientLight( color );
-					break;
+				default:
+					throw new Error( 'THREE.GLTFLoader: Unexpected light type, "' + lightDef.type + '".' );
 
 			}
 
-			if ( lightNode ) {
-
-				lightNode.decay = 2;
+			lightNode.decay = 2;
 
-				if ( light.intensity !== undefined ) {
-
-					lightNode.intensity = light.intensity;
-
-				}
+			if ( lightDef.intensity !== undefined ) lightNode.intensity = lightDef.intensity;
 
-				lightNode.name = light.name || ( 'light_' + lightId );
-				this.lights[ lightId ] = lightNode;
+			lightNode.name = lightDef.name || ( 'light_' + i );
 
-			}
+			this.lights.push( lightNode );
 
 		}
 
@@ -1019,21 +1016,6 @@ THREE.GLTFLoader = ( function () {
 		10497: THREE.RepeatWrapping
 	};
 
-	var WEBGL_TEXTURE_FORMATS = {
-		6406: THREE.AlphaFormat,
-		6407: THREE.RGBFormat,
-		6408: THREE.RGBAFormat,
-		6409: THREE.LuminanceFormat,
-		6410: THREE.LuminanceAlphaFormat
-	};
-
-	var WEBGL_TEXTURE_DATATYPES = {
-		5121: THREE.UnsignedByteType,
-		32819: THREE.UnsignedShort4444Type,
-		32820: THREE.UnsignedShort5551Type,
-		33635: THREE.UnsignedShort565Type
-	};
-
 	var WEBGL_SIDES = {
 		1028: THREE.BackSide, // Culling front
 		1029: THREE.FrontSide // Culling back
@@ -1188,6 +1170,28 @@ THREE.GLTFLoader = ( function () {
 
 	}
 
+	/**
+	 * @param {THREE.Object3D|THREE.Material|THREE.BufferGeometry} object
+	 * @param {GLTF.definition} def
+	 */
+	function assignExtrasToUserData( object, gltfDef ) {
+
+		if ( gltfDef.extras !== undefined ) {
+
+			if ( typeof gltfDef.extras === 'object' ) {
+
+				object.userData = gltfDef.extras;
+
+			} else {
+
+				console.warn( 'THREE.GLTFLoader: Ignoring primitive type .extras, ' + gltfDef.extras );
+
+			}
+
+		}
+
+	}
+
 	/**
 	 * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#morph-targets
 	 *
@@ -2014,22 +2018,6 @@ THREE.GLTFLoader = ( function () {
 
 			if ( textureDef.name !== undefined ) texture.name = textureDef.name;
 
-			// .format of dds texture is set in DDSLoader
-			if ( ! textureExtensions[ EXTENSIONS.MSFT_TEXTURE_DDS ] ) {
-
-				texture.format = textureDef.format !== undefined ? WEBGL_TEXTURE_FORMATS[ textureDef.format ] : THREE.RGBAFormat;
-
-			}
-
-			if ( textureDef.internalFormat !== undefined && texture.format !== WEBGL_TEXTURE_FORMATS[ textureDef.internalFormat ] ) {
-
-				console.warn( 'THREE.GLTFLoader: Three.js does not support texture internalFormat which is different from texture format. ' +
-											'internalFormat will be forced to be the same value as format.' );
-
-			}
-
-			texture.type = textureDef.type !== undefined ? WEBGL_TEXTURE_DATATYPES[ textureDef.type ] : THREE.UnsignedByteType;
-
 			var samplers = json.samplers || {};
 			var sampler = samplers[ textureDef.sampler ] || {};
 
@@ -2222,7 +2210,7 @@ THREE.GLTFLoader = ( function () {
 			if ( material.emissiveMap ) material.emissiveMap.encoding = THREE.sRGBEncoding;
 			if ( material.specularMap ) material.specularMap.encoding = THREE.sRGBEncoding;
 
-			if ( materialDef.extras ) material.userData = materialDef.extras;
+			assignExtrasToUserData( material, materialDef );
 
 			if ( materialDef.extensions ) addUnknownExtensionsToUserData( extensions, material, materialDef );
 
@@ -2266,11 +2254,7 @@ THREE.GLTFLoader = ( function () {
 
 		}
 
-		if ( primitiveDef.extras !== undefined ) {
-
-			geometry.userData = primitiveDef.extras;
-
-		}
+		assignExtrasToUserData( geometry, primitiveDef );
 
 	}
 
@@ -2539,7 +2523,7 @@ THREE.GLTFLoader = ( function () {
 
 					if ( geometries.length > 1 ) mesh.name += '_' + i;
 
-					if ( meshDef.extras !== undefined ) mesh.userData = meshDef.extras;
+					assignExtrasToUserData( mesh, meshDef );
 
 					meshes.push( mesh );
 
@@ -2707,7 +2691,8 @@ THREE.GLTFLoader = ( function () {
 		}
 
 		if ( cameraDef.name !== undefined ) camera.name = cameraDef.name;
-		if ( cameraDef.extras ) camera.userData = cameraDef.extras;
+
+		assignExtrasToUserData( camera, cameraDef );
 
 		return Promise.resolve( camera );
 
@@ -2918,36 +2903,26 @@ THREE.GLTFLoader = ( function () {
 
 				var mesh = dependencies.meshes[ nodeDef.mesh ];
 
-				node = mesh.clone();
+				if ( meshReferences[ nodeDef.mesh ] > 1 ) {
 
-				// for Specular-Glossiness
-				if ( mesh.isGroup === true ) {
+					var instanceNum = meshUses[ nodeDef.mesh ] ++;
 
-					for ( var i = 0, il = mesh.children.length; i < il; i ++ ) {
+					node = mesh.clone();
+					node.name += '_instance_' + instanceNum;
 
-						var child = mesh.children[ i ];
+					// onBeforeRender copy for Specular-Glossiness
+					node.onBeforeRender = mesh.onBeforeRender;
 
-						if ( child.material && child.material.isGLTFSpecularGlossinessMaterial === true ) {
+					for ( var i = 0, il = node.children.length; i < il; i ++ ) {
 
-							node.children[ i ].onBeforeRender = child.onBeforeRender;
-
-						}
+						node.children[ i ].name += '_instance_' + instanceNum;
+						node.children[ i ].onBeforeRender = mesh.children[ i ].onBeforeRender;
 
 					}
 
 				} else {
 
-					if ( mesh.material && mesh.material.isGLTFSpecularGlossinessMaterial === true ) {
-
-						node.onBeforeRender = mesh.onBeforeRender;
-
-					}
-
-				}
-
-				if ( meshReferences[ nodeDef.mesh ] > 1 ) {
-
-					node.name += '_instance_' + meshUses[ nodeDef.mesh ] ++;
+					node = mesh;
 
 				}
 
@@ -2956,11 +2931,11 @@ THREE.GLTFLoader = ( function () {
 				node = dependencies.cameras[ nodeDef.camera ];
 
 			} else if ( nodeDef.extensions
-					 && nodeDef.extensions[ EXTENSIONS.KHR_LIGHTS ]
-					 && nodeDef.extensions[ EXTENSIONS.KHR_LIGHTS ].light !== undefined ) {
+					 && nodeDef.extensions[ EXTENSIONS.KHR_LIGHTS_PUNCTUAL ]
+					 && nodeDef.extensions[ EXTENSIONS.KHR_LIGHTS_PUNCTUAL ].light !== undefined ) {
 
-				var lights = extensions[ EXTENSIONS.KHR_LIGHTS ].lights;
-				node = lights[ nodeDef.extensions[ EXTENSIONS.KHR_LIGHTS ].light ];
+				var lights = extensions[ EXTENSIONS.KHR_LIGHTS_PUNCTUAL ].lights;
+				node = lights[ nodeDef.extensions[ EXTENSIONS.KHR_LIGHTS_PUNCTUAL ].light ];
 
 			} else {
 
@@ -2974,7 +2949,7 @@ THREE.GLTFLoader = ( function () {
 
 			}
 
-			if ( nodeDef.extras ) node.userData = nodeDef.extras;
+			assignExtrasToUserData( node, nodeDef );
 
 			if ( nodeDef.extensions ) addUnknownExtensionsToUserData( extensions, node, nodeDef );
 
@@ -3108,7 +3083,7 @@ THREE.GLTFLoader = ( function () {
 				var scene = new THREE.Scene();
 				if ( sceneDef.name !== undefined ) scene.name = sceneDef.name;
 
-				if ( sceneDef.extras ) scene.userData = sceneDef.extras;
+				assignExtrasToUserData( scene, sceneDef );
 
 				if ( sceneDef.extensions ) addUnknownExtensionsToUserData( extensions, scene, sceneDef );
 
@@ -3120,16 +3095,6 @@ THREE.GLTFLoader = ( function () {
 
 				}
 
-				// Ambient lighting, if present, is always attached to the scene root.
-				if ( sceneDef.extensions
-						 && sceneDef.extensions[ EXTENSIONS.KHR_LIGHTS ]
-						 && sceneDef.extensions[ EXTENSIONS.KHR_LIGHTS ].light !== undefined ) {
-
-					var lights = extensions[ EXTENSIONS.KHR_LIGHTS ].lights;
-					scene.add( lights[ sceneDef.extensions[ EXTENSIONS.KHR_LIGHTS ].light ] );
-
-				}
-
 				return scene;
 
 			} );

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

@@ -108,7 +108,7 @@ THREE.HDRCubeTextureLoader.prototype.load = function ( type, urls, onLoad, onPro
 
 	function loadHDRData( i, onLoad, onProgress, onError ) {
 
-		var loader = new THREE.FileLoader( this.manager );
+		var loader = new THREE.FileLoader( scope.manager );
 		loader.setResponseType( 'arraybuffer' );
 		loader.load( urls[ i ], function ( buffer ) {
 

+ 74 - 405
examples/js/loaders/NodeMaterialLoader.js

@@ -91,7 +91,11 @@ Object.assign( THREE.NodeMaterialLoader.prototype, {
 
 	getObjectById: function ( uuid ) {
 
-		return this.library[ uuid ] || this.nodes[ uuid ] || this.names[ uuid ];
+		return this.library[ uuid ] || 
+			this.nodes[ uuid ] || 
+			this.materials[ uuid ] ||
+			this.passes[ uuid ] || 
+			this.names[ uuid ];
 
 	},
 
@@ -109,15 +113,62 @@ Object.assign( THREE.NodeMaterialLoader.prototype, {
 
 	},
 
-	parse: function ( json ) {
-
-		var uuid, node, object, prop, i;
+	resolve: function( json ) {
+		
+		switch( typeof json ) {
+			
+			case "boolean":
+			case "number":
+			
+				return json;
+			
+			case "string":
+			
+				if (/^\w{8}-\w{4}-\w{4}-\w{4}-\w{12}$/i.test(json) || this.library[ json ]) {
+					
+					return this.getNode( json );
+					
+				}
+				
+				return json;
+
+			default:
+			
+				if ( Array.isArray( json ) ) {
+			
+					for(var i = 0; i < json.length; i++) {
+						
+						json[i] = this.resolve( json[i] );
+						
+					}
+					
+				} else {
+					
+					for ( var prop in json ) {
+						
+						if (prop === "uuid") continue;
+						
+						json[ prop ] = this.resolve( json[ prop ] );
+						
+					}
+					
+				}
+				
+		}
+		
+		return json;
+		
+	},
+	
+	declare: function( json ) {
+		
+		var uuid, node, object;
 
 		for ( uuid in json.nodes ) {
 
 			node = json.nodes[ uuid ];
 
-			object = new THREE[ node.type ]();
+			object = new THREE[ node.nodeType + "Node" ]();
 
 			if ( node.name ) {
 
@@ -125,15 +176,8 @@ Object.assign( THREE.NodeMaterialLoader.prototype, {
 
 				this.names[ object.name ] = object;
 
-			} else {
-
-				// ignore "uniform" shader input ( for optimization )
-				object.readonly = true;
-
 			}
 
-			if ( node.readonly !== undefined ) object.readonly = node.readonly;
-
 			this.nodes[ uuid ] = object;
 
 		}
@@ -174,410 +218,35 @@ Object.assign( THREE.NodeMaterialLoader.prototype, {
 
 		}
 
-		if ( json.material ) this.material = this.materials[ uuid ];
-		if ( json.pass ) this.pass = this.passes[ uuid ];
+		if ( json.material ) this.material = this.materials[ json.material ];
+		
+		if ( json.pass ) this.pass = this.passes[ json.pass ];
+		
+		return json;
+		
+	},
+	
+	parse: function ( json ) {
 
+		var uuid;
+	
+		json = this.resolve( this.declare( json ) );
+		
 		for ( uuid in json.nodes ) {
 
-			node = json.nodes[ uuid ];
-			object = this.nodes[ uuid ];
-
-			switch ( node.type ) {
-
-				case "IntNode":
-				case "FloatNode":
-
-					object.value = node.value;
-
-					break;
-
-				case "ColorNode":
-
-					object.value.copy( node );
-
-					break;
-
-				case "Vector2Node":
-
-					object.x = node.x;
-					object.y = node.y;
-
-					break;
-
-
-				case "Vector3Node":
-
-					object.x = node.x;
-					object.y = node.y;
-					object.z = node.z;
-
-					break;
-
-				case "Vector4Node":
-
-					object.x = node.x;
-					object.y = node.y;
-					object.z = node.z;
-					object.w = node.w;
-
-					break;
-
-				case "Matrix3Node":
-				case "Matrix4Node":
-
-					object.value.fromArray( node.elements );
-
-					break;
-
-				case "OperatorNode":
-
-					object.a = this.getNode( node.a );
-					object.b = this.getNode( node.b );
-					object.op = node.op;
-
-					break;
-
-				case "Math1Node":
-
-					object.a = this.getNode( node.a );
-					object.method = node.method;
-
-					break;
-
-				case "Math2Node":
-
-					object.a = this.getNode( node.a );
-					object.b = this.getNode( node.b );
-					object.method = node.method;
-
-					break;
-
-				case "Math3Node":
-
-					object.a = this.getNode( node.a );
-					object.b = this.getNode( node.b );
-					object.c = this.getNode( node.c );
-					object.method = node.method;
-
-					break;
-
-				case "UVNode":
-				case "ColorsNode":
-
-					object.index = node.index;
-
-					break;
-
-
-				case "LuminanceNode":
-
-					object.rgb = this.getNode( node.rgb );
-
-					break;
-
-				case "PositionNode":
-				case "NormalNode":
-				case "ReflectNode":
-				case "LightNode":
-
-					object.scope = node.scope;
-
-					break;
-
-				case "SwitchNode":
-
-					object.node = this.getNode( node.node );
-					object.components = node.components;
-
-					break;
-
-				case "JoinNode":
-
-					for ( prop in node.inputs ) {
-
-						object[ prop ] = this.getNode( node.inputs[ prop ] );
-
-					}
-
-					break;
-
-				case "CameraNode":
-
-					object.setScope( node.scope );
-
-					if ( node.camera ) object.setCamera( this.getNode( node.camera ) );
-
-					switch ( node.scope ) {
-
-						case THREE.CameraNode.DEPTH:
-
-							object.near.number = node.near;
-							object.far.number = node.far;
-
-							break;
-
-					}
-
-					break;
-
-				case "ColorAdjustmentNode":
-
-					object.rgb = this.getNode( node.rgb );
-					object.adjustment = this.getNode( node.adjustment );
-					object.method = node.method;
-
-					break;
-
-				case "UVTransformNode":
-
-					object.uv = this.getNode( node.uv );
-					object.transform = this.getNode( node.transform );
-
-					break;
-
-				case "BumpNode":
-
-					object.value = this.getNode( node.value );
-					object.coord = this.getNode( node.coord );
-					object.scale = this.getNode( node.scale );
-
-					break;
-
-				case "BlurNode":
-
-					object.value = this.getNode( node.value );
-					object.coord = this.getNode( node.coord );
-					object.scale = this.getNode( node.scale );
-
-					object.value = this.getNode( node.value );
-					object.coord = this.getNode( node.coord );
-					object.radius = this.getNode( node.radius );
-
-					if ( node.size !== undefined ) object.size = new THREE.Vector2( node.size.x, node.size.y );
-
-					object.blurX = node.blurX;
-					object.blurY = node.blurY;
-
-					break;
-
-				case "ResolutionNode":
-
-					object.renderer = this.getNode( node.renderer );
-
-					break;
-
-				case "ScreenUVNode":
-
-					object.resolution = this.getNode( node.resolution );
-
-					break;
-
-				case "VelocityNode":
-
-					if ( node.target ) object.setTarget( this.getNode( node.target ) );
-					object.setParams( node.params );
-
-					break;
-
-				case "TimerNode":
-
-					object.scope = node.scope;
-					object.scale = node.scale;
-
-					break;
-
-				case "ConstNode":
-
-					object.name = node.name;
-					object.type = node.out;
-					object.value = node.value;
-					object.useDefine = node.useDefine === true;
-
-					break;
-
-				case "AttributeNode":
-				case "VarNode":
-
-					object.type = node.out;
-
-					break;
-
-
-				case "ReflectorNode":
-
-					object.setMirror( this.getNode( node.mirror ) );
-
-					if ( node.offset ) object.offset = this.getNode( node.offset );
-
-					break;
-
-				case "NoiseNode":
-
-					object.coord = this.getNode( node.coord );
-
-					break;
-
-				case "FunctionNode":
-
-					object.isMethod = node.isMethod;
-					object.useKeywords = node.useKeywords;
-
-					object.extensions = node.extensions;
-					object.keywords = {};
-
-					for ( prop in node.keywords ) {
-
-						object.keywords[ prop ] = this.getNode( node.keywords[ prop ] );
-
-					}
-
-					if ( node.includes ) {
-
-						for ( i = 0; i < node.includes.length; i ++ ) {
-
-							object.includes.push( this.getNode( node.includes[ i ] ) );
-
-						}
-
-					}
-
-					object.eval( node.src, object.includes, object.extensions, object.keywords );
-
-					if ( ! object.isMethod ) object.type = node.out;
-
-					break;
-
-				case "FunctionCallNode":
-
-					for ( prop in node.inputs ) {
-
-						object.inputs[ prop ] = this.getNode( node.inputs[ prop ] );
-
-					}
-
-					object.value = this.getNode( node.value );
-
-					break;
-
-				case "TextureNode":
-				case "CubeTextureNode":
-				case "ScreenNode":
-
-					if ( node.value ) object.value = this.getNode( node.value );
-
-					object.coord = this.getNode( node.coord );
-
-					if ( node.bias ) object.bias = this.getNode( node.bias );
-					if ( object.project !== undefined ) object.project = node.project;
-
-					break;
-
-				case "RoughnessToBlinnExponentNode":
-					break;
-
-				case "RawNode":
-
-					object.value = this.getNode( node.value );
-
-					break;
-
-				case "StandardNode":
-				case "PhongNode":
-				case "SpriteNode":
-
-					object.color = this.getNode( node.color );
-
-					if ( node.alpha ) object.alpha = this.getNode( node.alpha );
-
-					if ( node.specular ) object.specular = this.getNode( node.specular );
-					if ( node.shininess ) object.shininess = this.getNode( node.shininess );
-
-					if ( node.roughness ) object.roughness = this.getNode( node.roughness );
-					if ( node.metalness ) object.metalness = this.getNode( node.metalness );
-
-					if ( node.reflectivity ) object.reflectivity = this.getNode( node.reflectivity );
-
-					if ( node.clearCoat ) object.clearCoat = this.getNode( node.clearCoat );
-					if ( node.clearCoatRoughness ) object.clearCoatRoughness = this.getNode( node.clearCoatRoughness );
-
-					if ( node.normal ) object.normal = this.getNode( node.normal );
-					if ( node.normalScale ) object.normalScale = this.getNode( node.normalScale );
-
-					if ( node.emissive ) object.emissive = this.getNode( node.emissive );
-					if ( node.ambient ) object.ambient = this.getNode( node.ambient );
-
-					if ( node.shadow ) object.shadow = this.getNode( node.shadow );
-					if ( node.light ) object.light = this.getNode( node.light );
-
-					if ( node.ao ) object.ao = this.getNode( node.ao );
-
-					if ( node.environment ) object.environment = this.getNode( node.environment );
-					if ( node.environmentAlpha ) object.environmentAlpha = this.getNode( node.environmentAlpha );
-
-					if ( node.transform ) object.transform = this.getNode( node.transform );
-
-					if ( node.spherical === false ) object.spherical = false;
-
-					break;
-
-				default:
-
-					console.warn( node.type, "not supported." );
-
-			}
+			this.nodes[ uuid ].copy( json.nodes[ uuid ] );
 
 		}
-
+		
 		for ( uuid in json.materials ) {
 
-			node = json.materials[ uuid ];
-			object = this.materials[ uuid ];
-
-			if ( node.name !== undefined ) object.name = node.name;
-
-			if ( node.blending !== undefined ) object.blending = node.blending;
-			if ( node.flatShading !== undefined ) object.flatShading = node.flatShading;
-			if ( node.side !== undefined ) object.side = node.side;
-
-			object.depthFunc = node.depthFunc;
-			object.depthTest = node.depthTest;
-			object.depthWrite = node.depthWrite;
-
-			if ( node.wireframe !== undefined ) object.wireframe = node.wireframe;
-			if ( node.wireframeLinewidth !== undefined ) object.wireframeLinewidth = node.wireframeLinewidth;
-			if ( node.wireframeLinecap !== undefined ) object.wireframeLinecap = node.wireframeLinecap;
-			if ( node.wireframeLinejoin !== undefined ) object.wireframeLinejoin = node.wireframeLinejoin;
-
-			if ( node.skinning !== undefined ) object.skinning = node.skinning;
-			if ( node.morphTargets !== undefined ) object.morphTargets = node.morphTargets;
-
-			if ( node.visible !== undefined ) object.visible = node.visible;
-			if ( node.userData !== undefined ) object.userData = node.userData;
-
-			object.vertex = this.getNode( node.vertex );
-			object.fragment = this.getNode( node.fragment );
-
-			if ( object.vertex === object.fragment ) {
-
-				// replace main node
-
-				object.node = object.vertex;
-
-			}
-
-			if ( node.fog !== undefined ) object.fog = node.fog;
-			if ( node.lights !== undefined ) object.lights = node.lights;
-
-			if ( node.transparent !== undefined ) object.transparent = node.transparent;
+			this.materials[ uuid ].copy( json.materials[ uuid ] );
 
 		}
-
+		
 		for ( uuid in json.passes ) {
 
-			node = json.passes[ uuid ];
-			object = this.passes[ uuid ];
-
-			object.value = this.getNode( node.value );
+			this.passes[ uuid ].copy( json.passes[ uuid ] );
 
 		}
 

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