Mr.doob 11 years ago
parent
commit
d10a040805
100 changed files with 6008 additions and 1559 deletions
  1. 12 4
      docs/api/core/Geometry.html
  2. 7 9
      docs/api/core/Object3D.html
  3. 39 1
      docs/api/examples/Lut.html
  4. 1 1
      docs/api/extras/GeometryUtils.html
  5. 1 1
      docs/api/extras/ImageUtils.html
  6. 18 18
      docs/api/extras/geometries/BoxGeometry.html
  7. 40 12
      docs/api/extras/geometries/ExtrudeGeometry.html
  8. 0 6
      docs/api/extras/geometries/ShapeGeometry.html
  9. 8 6
      docs/api/extras/helpers/BoundingBoxHelper.html
  10. 2 5
      docs/api/extras/helpers/GridHelper.html
  11. 2 2
      docs/api/extras/renderers/plugins/ShadowMapPlugin.html
  12. 1 1
      docs/api/loaders/ImageLoader.html
  13. 1 1
      docs/api/loaders/MaterialLoader.html
  14. 0 98
      docs/api/loaders/SceneLoader.html
  15. 5 0
      docs/api/materials/MeshBasicMaterial.html
  16. 4 0
      docs/api/materials/MeshLambertMaterial.html
  17. 4 0
      docs/api/materials/MeshPhongMaterial.html
  18. 1 1
      docs/api/materials/PointCloudMaterial.html
  19. 14 19
      docs/api/materials/ShaderMaterial.html
  20. 0 9
      docs/api/math/Box3.html
  21. 5 3
      docs/api/objects/Line.html
  22. 5 5
      docs/api/objects/Mesh.html
  23. 1 1
      docs/api/objects/PointCloud.html
  24. 59 17
      docs/api/renderers/webgl/WebGLProgram.html
  25. 216 12
      docs/index.html
  26. 2 3
      docs/list.js
  27. 3 3
      docs/manual/introduction/Creating-a-scene.html
  28. 6 0
      editor/css/dark.css
  29. 6 0
      editor/css/light.css
  30. 4 3
      editor/index.html
  31. 8 2
      editor/js/Editor.js
  32. 12 1
      editor/js/Loader.js
  33. 6 6
      editor/js/Menubar.Edit.js
  34. 36 45
      editor/js/Sidebar.Animation.js
  35. 1 2
      editor/js/Sidebar.Geometry.BoxGeometry.js
  36. 62 0
      editor/js/Sidebar.Geometry.BufferGeometry.js
  37. 1 3
      editor/js/Sidebar.Geometry.CircleGeometry.js
  38. 1 2
      editor/js/Sidebar.Geometry.CylinderGeometry.js
  39. 53 0
      editor/js/Sidebar.Geometry.Geometry.js
  40. 1 2
      editor/js/Sidebar.Geometry.IcosahedronGeometry.js
  41. 2 3
      editor/js/Sidebar.Geometry.PlaneGeometry.js
  42. 1 2
      editor/js/Sidebar.Geometry.SphereGeometry.js
  43. 1 2
      editor/js/Sidebar.Geometry.TorusGeometry.js
  44. 1 2
      editor/js/Sidebar.Geometry.TorusKnotGeometry.js
  45. 8 58
      editor/js/Sidebar.Geometry.js
  46. 96 12
      editor/js/Sidebar.Material.js
  47. 67 71
      editor/js/Sidebar.Object3D.js
  48. 34 11
      editor/js/Viewport.js
  49. 13 0
      editor/js/libs/ui.js
  50. 10 9
      examples/canvas_camera_orthographic2.html
  51. 144 0
      examples/canvas_effects_stereo.html
  52. 1 1
      examples/canvas_geometry_birds.html
  53. 2 2
      examples/canvas_geometry_cube.html
  54. 1 1
      examples/canvas_geometry_hierarchy.html
  55. 4 2
      examples/canvas_geometry_nurbs.html
  56. 1 1
      examples/canvas_interactive_cubes.html
  57. 15 28
      examples/canvas_lights_pointlights.html
  58. 2 4
      examples/canvas_lines_colors.html
  59. 8 12
      examples/canvas_materials.html
  60. 1 2
      examples/canvas_particles_shapes.html
  61. 1 1
      examples/css3d_molecules.html
  62. 5 12
      examples/css3d_sprites.html
  63. 12 5
      examples/index.html
  64. 3 5
      examples/js/BlendCharacter.js
  65. 0 182
      examples/js/BufferGeometryUtils.js
  66. 1 1
      examples/js/Car.js
  67. 260 0
      examples/js/SkyShader.js
  68. 2 2
      examples/js/UCSCharacter.js
  69. 236 0
      examples/js/cameras/CombinedCamera.js
  70. 34 48
      examples/js/controls/DeviceOrientationControls.js
  71. 12 5
      examples/js/controls/OrbitControls.js
  72. 535 0
      examples/js/controls/OrthographicTrackballControls.js
  73. 0 327
      examples/js/controls/PathControls.js
  74. 53 41
      examples/js/controls/TrackballControls.js
  75. 3 3
      examples/js/controls/TransformControls.js
  76. 77 0
      examples/js/controls/VRControls.js
  77. 22 15
      examples/js/effects/StereoEffect.js
  78. 233 0
      examples/js/effects/VREffect.js
  79. 7 0
      examples/js/exporters/MaterialExporter.js
  80. 79 0
      examples/js/exporters/STLBinaryExporter.js
  81. 1 1
      examples/js/libs/dat.gui.min.js
  82. 600 0
      examples/js/libs/msgpack-js.js
  83. 32 0
      examples/js/libs/pnltri.min.js
  84. 36 0
      examples/js/libs/require.js
  85. 10 10
      examples/js/loaders/AWDLoader.js
  86. 4 1
      examples/js/loaders/AssimpJSONLoader.js
  87. 94 119
      examples/js/loaders/ColladaLoader.js
  88. 347 0
      examples/js/loaders/DDSLoader.js
  89. 8 6
      examples/js/loaders/MTLLoader.js
  90. 4 2
      examples/js/loaders/OBJLoader.js
  91. 0 2
      examples/js/loaders/OBJMTLLoader.js
  92. 16 62
      examples/js/loaders/PDBLoader.js
  93. 14 8
      examples/js/loaders/STLLoader.js
  94. 31 0
      examples/js/loaders/SVGLoader.js
  95. 1259 0
      examples/js/loaders/SceneLoader.js
  96. 451 0
      examples/js/loaders/TGALoader.js
  97. 18 22
      examples/js/loaders/UTF8Loader.js
  98. 35 19
      examples/js/loaders/ctm/CTMLoader.js
  99. 5 55
      examples/js/loaders/gltf/glTFLoader.js
  100. 408 78
      examples/js/math/Lut.js

+ 12 - 4
docs/api/core/Geometry.html

@@ -19,9 +19,11 @@
 
 		<code>var geometry = new THREE.Geometry();
 
-		geometry.vertices.push( new THREE.Vector3( -10,  10, 0 ) );
-		geometry.vertices.push( new THREE.Vector3( -10, -10, 0 ) );
-		geometry.vertices.push( new THREE.Vector3(  10, -10, 0 ) );
+		geometry.vertices.push(
+			new THREE.Vector3( -10,  10, 0 ),
+			new THREE.Vector3( -10, -10, 0 ),
+			new THREE.Vector3(  10, -10, 0 )
+		);
 
 		geometry.faces.push( new THREE.Face3( 0, 1, 2 ) );
 
@@ -59,7 +61,7 @@
 		<h3>.[page:Array colors]</h3>
 		<div>
 		Array of vertex [page:Color colors], matching number and order of vertices.<br />
-		Used in [page:ParticleSystem] and [page:Line].<br />
+		Used in [page:PointCloud] and [page:Line].<br />
 		[page:Mesh Meshes] use per-face-use-of-vertex colors embedded directly in faces.<br />
 		To signal an update in this array, [page:Geometry Geometry.colorsNeedUpdate] needs to be set to true.
 		</div>
@@ -223,12 +225,18 @@
 		
 		<div>Neither bounding boxes or bounding spheres are computed by default. They need to be explicitly computed, otherwise they are *null*.</div>
 
+		<h3>.merge( [page:Geometry geometry], [page:Matrix4 matrix], [page:Integer materialIndexOffset] )</h3>
+		<div>Merge two geometries or geometry and geometry from object (using object's transform)</div>
+
 		<h3>.mergeVertices()</h3>
 		<div>
 		Checks for duplicate vertices using hashmap.<br />
 		Duplicated vertices are removed and faces' vertices are updated.
 		</div>
 		
+		<h3>.makeGroups()</h3>
+		<div>Geometry splitting</div>
+		
 		<h3>.clone()</h3>
 		<div>
 		Creates a new clone of the Geometry.

+ 7 - 9
docs/api/core/Object3D.html

@@ -25,7 +25,13 @@
 
 		<h3>.[page:Integer id]</h3>
 		<div>
-		Unique number of this object instance.
+		Identifier of this object instance.
+		</div>
+		
+		<h3>.[page:String uuid]</h3>
+		<div>
+		[link:http://en.wikipedia.org/wiki/Universally_unique_identifier UUID] of this object instance. 
+		This gets automatically assigned, so this shouldn't be edited.
 		</div>
 
 		<h3>.[page:String name]</h3>
@@ -218,14 +224,6 @@
 		Executes the callback on this object and all descendants. 
 		</div>
 
-		<h3>.getDescendants( [page:Array array] )</h3>
-		<div>
-		array - optional argument that returns the the array with descendants.<br />
-		</div>
-		<div>
-		Searches whole subgraph recursively to add all objects in the array.
-		</div>
-
 		<h3>.updateMatrix()</h3>
 		<div>
 		Updates local transform.

+ 39 - 1
docs/api/examples/Lut.html

@@ -40,6 +40,11 @@
 		<div>
 		The maximum value to be represented with the lookup table. Default is 1.
 		</div>
+		
+		<h3>.[legend]</h3>
+		<div>
+		The legend of the lookup table.
+		</div>
 
 		<h2>Methods</h2>
 
@@ -50,7 +55,40 @@
 		<div>
 		Copies given lut.
 		</div>
-
+		
+		<h3>.setLegendOn [parameters]</h3>
+		<div>
+		parameters - { layout: value, position: { x: value, y: value, z: value }, dimensions: { width: value, height: value } }
+		layout — Horizontal or vertical layout. Default is vertical.<br />
+		position — The position x,y,z of the legend.<br />
+		dimensions — The dimensions (width and height) of the legend.<br />
+		</div>
+		<div>
+		Sets this Lut with the legend on.
+		</div>
+		
+		<h3>.setLegendOff</h3>
+		<div>
+		</div>
+		<div>
+		Sets this Lut with the legend off.
+		</div>
+    
+    <h3>.setLegendLabels [parameters, callback]</h3>
+		<div>
+		parameters - { fontsize: value, fontface: value, title: value, um: value, ticks: value, decimal: value, notation: value }
+		fontsize — Font size to be used for labels.<br />
+		fontface — Font type to be used for labels.<br />
+		title — The title of the legend.<br />
+		um — The unit of measurements of the legend.<br />
+		ticks — The number of ticks to be displayed.<br />
+		decimal — The number of decimals to be used for legend values.<br />
+		notation — Legend notation: standard (default) or scientific.<br />
+		callback — An optional callback to be used to format the legend labels.<br />
+		</div>
+		<div>
+		Sets the labels of the legend of this Lut.
+		</div>
 
 		<h3>.setminV( [page:Float minV] ) [page:Lut this]</h3>
 		<div>

+ 1 - 1
docs/api/extras/GeometryUtils.html

@@ -16,7 +16,7 @@
 
 		<h3> .merge( [page:Geometry geometry1] , [page:Geometry geometry2], [page:Integer materialIndexOffset] )</h3>
 		<div>
-		geometry1 — Parent geomentry element <br />
+		geometry1 — Parent geometry element <br />
 		geometry2 — Geometry that need to be added in parent <br />
 		materialIndexOffset — Offset applied to the materialIndex of all the new faces in the merged geometry. Default : 0 <br />
 		</div>

+ 1 - 1
docs/api/extras/ImageUtils.html

@@ -58,7 +58,7 @@
 		<h3>.loadTexture([page:String url], [page:UVMapping mapping], [page:Function onLoad], [page:Function onError]) [page:todo]</h3>
 		<div>
 		url -- the url of the texture<br />
-		mapping -- Can be an instance of [page:UVMapping Three.UVMapping], [page:CubeReflectionMapping THREE.CubeReflectionMapping], [page:SphericalReflectionMapping THREE.SphericalReflectionMapping] or [page:SphericalRefractionMapping THREE.SphericalRefractionMapping]. Describes how the image is applied to the object.<br />Use undefined instead of null as a default value. See mapping property of [page:Texture texture] for more details.
+		mapping -- Can be an instance of [page:UVMapping THREE.UVMapping], [page:CubeReflectionMapping THREE.CubeReflectionMapping], [page:SphericalReflectionMapping THREE.SphericalReflectionMapping] or [page:SphericalRefractionMapping THREE.SphericalRefractionMapping]. Describes how the image is applied to the object.<br />Use undefined instead of null as a default value. See mapping property of [page:Texture texture] for more details.
 		onLoad -- callback function<br />
 		onError -- callback function
 		</div>

+ 18 - 18
docs/api/extras/geometries/BoxGeometry.html

@@ -8,39 +8,39 @@
 	</head>
 	<body>
 		[page:Geometry] &rarr;
-		
+
 		<h1>[name]</h1>
 
-		<div class="desc">todo</div>
+		<div class="desc">BoxGeometry is the quadrilateral primitive geometry class. It is typically used for creating a cube or irregular quadrilateral of the dimensions provided with the 'width', 'height', and 'depth' constructor arguments.</div>
 
 		<h2>Example</h2>
 
-		<code>todo</code>
+		<code>var geometry = new THREE.BoxGeometry( 1, 1, 1 );
+		var material = new THREE.MeshBasicMaterial( {color: 0x00ff00} );
+		var cube = new THREE.Mesh( geometry, material );
+		scene.add( cube );
+		</code>
 
 		<h2>Constructor</h2>
 
 
-		<h3>todo</h3>
-		<div></div>
-
-
-		<h2>Properties</h2>
-
-		<h3>todo</h3>
+		<h3>[name]([page:Float width], [page:Float height], [page:Float depth], [page:Integer widthSegments], [page:Integer heightSegments], [page:Integer depthSegments])</h3>
 		<div>
-		todo
-		</div> 
+		width — Width of the sides on the X axis.<br />
+		height — Height of the sides on the Y axis.<br />
+		depth — Depth of the sides on the Z axis.<br />
+		widthSegments — Optional. Number of segmented faces along the width of the sides. Default is 1.<br />
+		heightSegments — Optional. Number of segmented faces along the height of the sides. Default is 1.<br />
+		depthSegments — Optional. Number of segmented faces along the depth of the sides. Default is 1.
+		</div>
 
 
-		<h2>Methods</h2>
-		
+		<h2>Properties</h2>
 
-		<h3>todo</h3>
-		<div>todo</div>
 		<div>
-		todo
+		Each of the constructor parameters is accessible as a property of the same name. Any modification of these properties after instantiation does not change the geometry.
 		</div>
-		
+
 		<h2>Source</h2>
 
 		[link:https://github.com/mrdoob/three.js/blob/master/src/[path].js src/[path].js]

+ 40 - 12
docs/api/extras/geometries/ExtrudeGeometry.html

@@ -17,7 +17,7 @@
 		<h2>Constructor</h2>
 
 
-		<h3>[name]([page:todo shapes], [page:Object options])</h3>
+		<h3>[name]([page:Array shapes], [page:Object options])</h3>
 		<div>
 		shapes — Shape or an array of shapes. <br />
 		options — Object that can contain the following parameters.
@@ -39,7 +39,7 @@
 
 		</div>
 		<div>
-		todo
+		This object extrudes an 2D shape to an 3D geometry.
 		</div>
 
 
@@ -48,19 +48,47 @@
 
 		<h2>Methods</h2>
 
-		<h3>.addShapeList ([page:todo shapes], [page:Object options])</h3>
+		<h3>.addShapeList ([page:Array shapes], [page:Object options])</h3>
 		<div>
-			shapes — todo <br />
-			options — todo
-		</div>
-		<div>todo</div>
+			shapes — An Array of shapes to add. <br />
+			options — Object that can contain the following parameters.
+	<ul>
+<li>curveSegments —  int. number of points on the curves</li>
+<li>steps —  int. number of points used for subdividing segements of extrude spline</li>
+<li>amount —  int. Depth to extrude the shape</li>
+<li>bevelEnabled —  bool. turn on bevel</li>
+<li>bevelThickness —  float. how deep into the original shape bevel goes</li>
+<li>bevelSize —  float. how far from shape outline is bevel</li>
+<li>bevelSegments —  int. number of bevel layers</li>
+<li>extrudePath —  THREE.CurvePath. 3d spline path to extrude shape along. (creates Frames if (frames aren't defined)</li>
+<li>frames —  THREE.TubeGeometry.FrenetFrames.  containing arrays of tangents, normals, binormals</li>
+<li>material —  int. material index for front and back faces</li>
+<li>extrudeMaterial —  int. material index for extrusion and beveled faces</li>
+<li>uvGenerator —  Object. object that provides UV generator functions</li>
+	</ul>
+	</div>
+		<div>Adds the shapes to the list to extrude.</div>
 
-		<h3>.addShape ([page:todo shape], [page:Object options])</h3>
+		<h3>.addShape ([page:Shape shape], [page:Object options])</h3>
 		<div>
-			shape — todo <br />
-			options — todo
-		</div>
-		<div>todo</div>
+			shape — A shape to add. <br />
+			options — Object that can contain the following parameters.
+	<ul>
+<li>curveSegments —  int. number of points on the curves</li>
+<li>steps —  int. number of points used for subdividing segements of extrude spline</li>
+<li>amount —  int. Depth to extrude the shape</li>
+<li>bevelEnabled —  bool. turn on bevel</li>
+<li>bevelThickness —  float. how deep into the original shape bevel goes</li>
+<li>bevelSize —  float. how far from shape outline is bevel</li>
+<li>bevelSegments —  int. number of bevel layers</li>
+<li>extrudePath —  THREE.CurvePath. 3d spline path to extrude shape along. (creates Frames if (frames aren't defined)</li>
+<li>frames —  THREE.TubeGeometry.FrenetFrames.  containing arrays of tangents, normals, binormals</li>
+<li>material —  int. material index for front and back faces</li>
+<li>extrudeMaterial —  int. material index for extrusion and beveled faces</li>
+<li>uvGenerator —  Object. object that provides UV generator functions</li>
+	</ul>
+	</div>
+		<div>Add the shape to the list to extrude.</div>
 
 
 		<h2>Source</h2>

+ 0 - 6
docs/api/extras/geometries/ShapeGeometry.html

@@ -52,12 +52,6 @@
 		<h2>Properties</h2>
 
 
-
-		<h3>.[page:Object shapebb]</h3>
-		<div>
-		todo
-		</div> 
-
 		<h2>Methods</h2>
 
 

+ 8 - 6
docs/api/extras/helpers/BoundingBoxHelper.html

@@ -16,13 +16,15 @@
 
 		<h2>Example</h2>
 
-		<code>var dir = new THREE.Vector3( 1, 0, 0 );
-		var origin = new THREE.Vector3( 0, 0, 0 );
-		var length = 1;
-		var hex = 0xffff00;
+		<code>var hex  = 0xff0000;
 
-		var arrowHelper = new THREE.ArrowHelper( dir, origin, length, hex );
-		scene.add( arrowHelper );
+		var sphereMaterial = new THREE.MeshLambertMaterial( {color: 0x00ff00} );
+		var sphere = new THREE.Mesh( new THREE.SphereGeometry( 30, 12, 12), sphereMaterial );
+		scene.add( sphere );
+
+		var bbox = new THREE.BoundingBoxHelper( sphere, hex );
+		bbox.update();
+		scene.add( bbox );
 		</code>
 
 

+ 2 - 5
docs/api/extras/helpers/GridHelper.html

@@ -18,11 +18,8 @@
 
 		<code>var size = 10;
 		var step = 1;
-		var gridHelper = new THREE.GridHelper( size, step );
-		
-		gridHelper.position = new THREE.Vector3( 10, 10, 0 );
-		gridHelper.rotation = new THREE.Euler( 15, 0, 0 );
-		
+
+		var gridHelper = new THREE.GridHelper( size, step );		
 		scene.add( gridHelper );
 		</code>
 

+ 2 - 2
docs/api/extras/renderers/plugins/ShadowMapPlugin.html

@@ -27,7 +27,7 @@
 
 
 
-		<h3>.init([page:WebglRenderer renderer])</h3>
+		<h3>.init([page:WebGLRenderer renderer])</h3>
 		<div>
 		renderer -- The WebglRenderer that uses the plugin.
 		</div>
@@ -41,7 +41,7 @@
 		camera -- The camera to render.
 		</div>
 		<div>
-		Updates the textures nessecary for the shadowmaps. This gets called by updateShadowMap in [page:WebglRenderer].
+		Updates the textures nessecary for the shadowmaps. This gets called by updateShadowMap in [page:WebGLRenderer].
 		</div>
 
 		<h3>.render([page:Scene scene], [page:Camera camera])</h3>

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

@@ -14,7 +14,7 @@
 		<h2>Constructor</h2>
 
 
-		<h3>[name]([page:LoadingManager manager)</h3>
+		<h3>[name]([page:LoadingManager manager])</h3>
         <div>
         manager -- The [page:LoadingManager loadingManager] for the loader to use. Default is [page:LoadingManager THREE.DefaultLoadingManager].
         </div>

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

@@ -14,7 +14,7 @@
 		<h2>Constructor</h2>
 
 
-		<h3>[name]([page:LoadingManager manager)</h3>
+		<h3>[name]([page:LoadingManager manager])</h3>
         <div>
         manager -- The [page:LoadingManager loadingManager] for the loader to use. Default is [page:LoadingManager THREE.DefaultLoadingManager].
         </div>

+ 0 - 98
docs/api/loaders/SceneLoader.html

@@ -1,98 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-	<head>
-		<meta charset="utf-8" />
-		<script src="../../list.js"></script>
-		<script src="../../page.js"></script>
-		<link type="text/css" rel="stylesheet" href="../../page.css" />
-	</head>
-	<body>
-		<h1>[name]</h1>
-
-		<div class="desc">A loader for loading a complete scene out of a JSON file.</div>
-
-
-		<h2>Constructor</h2>
-
-
-		<h3>[name]()</h3>
-		<div>
-		todo
-		</div>
-		
-
-		<h2>Properties</h2>
-
-		<h3>.[page:Function onLoadStart]</h3>
-		<div>Will be called when load starts.</div>
-		<div>The default is a function with empty body.</div>
-
-		<h3>.[page:Function onLoadProgress]</h3>
-		<div>Will be called while load progresses.</div>
-		<div>The default is a function with empty body.</div>
-		
-		<h3>.[page:Function onLoadComplete]</h3>
-		<div>Will be called when each element in the scene completes loading.</div>
-		<div>The default is a function with empty body.</div>
-		
-		<h3>.[page:Function callbackSync]</h3>
-		<div>Will be called when load completes.</div>
-		<div>The default is a function with empty body.</div>
-		
-		<h3>.[page:Function callbackProgress]</h3>
-		<div>Will be called as load progresses.</div>
-		<div>The default is a function with empty body.</div>
-
-
-		<h3>.[page:object hierarchyHandlerMap]</h3>
-		<div>
-		todo
-		</div> 
-
-		<h3>.[page:object geometryHandlerMap]</h3>
-		<div>
-		todo
-		</div> 
-
-		<h2>Methods</h2>
-
-		<h3>.load( [page:String url], [page:Function callbackFinished] )</h3>
-		<div>
-		url — required<br />
-		callbackFinished — required. This function will be called with the loaded model as an instance of [page:Scene scene] when the load is completed.
-		</div>
-
-
-		<h3>.addHierarchyHandler([page:todo typeID], [page:todo loaderClass]) [page:todo]</h3>
-		<div>
-		typeID -- todo <br />
-		loaderClass -- todo
-		</div>
-		<div>
-		todo
-		</div>
-
-		<h3>.parse([page:todo json], [page:todo callbackFinished], [page:todo url]) [page:todo]</h3>
-		<div>
-		json -- todo <br />
-		callbackFinished -- todo <br />
-		url -- todo
-		</div>
-		<div>
-		todo
-		</div>
-
-		<h3>.addGeometryHandler([page:todo typeID], [page:todo loaderClass]) [page:todo]</h3>
-		<div>
-		typeID -- todo <br />
-		loaderClass -- todo
-		</div>
-		<div>
-		todo
-		</div>
-
-		<h2>Source</h2>
-
-		[link:https://github.com/mrdoob/three.js/blob/master/src/[path].js src/[path].js]
-	</body>
-</html>

+ 5 - 0
docs/api/materials/MeshBasicMaterial.html

@@ -32,6 +32,7 @@
 		fog — Define whether the material color is affected by global fog settings. Default is true.<br />
 		lightMap — Set light map. Default is null.<br />
 		specularMap — Set specular map. Default is null.<br />
+		alphaMap — Set alpha map. Default is null.<br />
 		envMap — Set env map. Default is null.<br />
 		skinning — Define whether the material uses skinning. Default is false.<br />
 		morphTargets — Define whether the material uses morphTargets. Default is false.
@@ -74,6 +75,10 @@
 		<h3>.[page:Texture specularMap]</h3>
 		<div>Set specular map. Default is null.</div>
 
+		<h3>.[page:Texture alphaMap]</h3>
+		<div>The alpha map is a grayscale texture that controls the opacity across the surface (black: fully transparent; white: fully opaque). Default is null.</div>
+		<div>Only the color of the texture is used, ignoring the alpha channel if one exists. For RGB and RGBA textures, the [page:WebGLRenderer WebGL] renderer will use the green channel when sampling this texture due to the extra bit of precision provided for green in DXT-compressed and uncompressed RGB 565 formats. Luminance-only and luminance/alpha textures will also still work as expected.</div>
+
 		<h3>.[page:TextureCube envMap]</h3>
 		<div>Set env map. Default is null.</div>
 

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

@@ -81,6 +81,10 @@
 		<h3>.[page:Texture specularMap]</h3>
 		<div>Since this material does not have a specular component, the specular value affects only how much of the environment map affects the surface. Default is null.</div>
 
+		<h3>.[page:Texture alphaMap]</h3>
+		<div>The alpha map is a grayscale texture that controls the opacity across the surface (black: fully transparent; white: fully opaque). Default is null.</div>
+		<div>Only the color of the texture is used, ignoring the alpha channel if one exists. For RGB and RGBA textures, the [page:WebGLRenderer WebGL] renderer will use the green channel when sampling this texture due to the extra bit of precision provided for green in DXT-compressed and uncompressed RGB 565 formats. Luminance-only and luminance/alpha textures will also still work as expected.</div>
+
 		<h3>.[page:TextureCube envMap]</h3>
 		<div>Set env map. Default is null.</div>
 

+ 4 - 0
docs/api/materials/MeshPhongMaterial.html

@@ -91,6 +91,10 @@
 		<h3>.[page:Texture specularMap]</h3>
 		<div>The specular map value affects both how much the specular surface highlight contributes and how much of the environment map affects the surface. Default is null.</div>
 
+		<h3>.[page:Texture alphaMap]</h3>
+		<div>The alpha map is a grayscale texture that controls the opacity across the surface (black: fully transparent; white: fully opaque). Default is null.</div>
+		<div>Only the color of the texture is used, ignoring the alpha channel if one exists. For RGB and RGBA textures, the [page:WebGLRenderer WebGL] renderer will use the green channel when sampling this texture due to the extra bit of precision provided for green in DXT-compressed and uncompressed RGB 565 formats. Luminance-only and luminance/alpha textures will also still work as expected.</div>
+
 		<h3>.[page:TextureCube envMap]</h3>
 		<div>Set env map. Default is null.</div>
 

+ 1 - 1
docs/api/materials/ParticleSystemMaterial.html → docs/api/materials/PointCloudMaterial.html

@@ -11,7 +11,7 @@
 
 		<h1>[name]</h1>
 
-		<div class="desc">The default material used by [page:ParticleSystem particle] systems.</div>
+		<div class="desc">The default material used by [page:PointCloud particle] systems.</div>
 
 
 		<h2>Constructor</h2>

+ 14 - 19
docs/api/materials/ShaderMaterial.html

@@ -14,36 +14,31 @@
 		<div class="desc">Material rendered with custom shaders</div>
 
 
-		<h2>Constructor</h2>
-
-
-		<h3>[name]([page:Object parameters])</h3>
-		<div>
-		parameters -- An object containing various parameters setting up shaders and their uniforms.
-		</div>
-		<div>
-		<br>
-		Example:<br>
-		<br>
-		uniforms = {
-			time: { type: "f", value: 1.0 },
-			resolution: { type: "v2", value: new THREE.Vector2() }
-		};
+		<h2>Example</h2>
 
-		material = new THREE.ShaderMaterial( {
+		<code>
+		var material = new THREE.ShaderMaterial( {
 
-			uniforms: uniforms,
+			uniforms: {
+				time: { type: "f", value: 1.0 },
+				resolution: { type: "v2", value: new THREE.Vector2() }
+			},
 			vertexShader: document.getElementById( 'vertexShader' ).textContent,
 			fragmentShader: document.getElementById( 'fragmentShader' ).textContent
 
 		} );
+		</code>
 
-		</div>
 
+		<h2>Constructor</h2>
 
-		<h2>Properties</h2>
+		<h3>[name]([page:Object parameters])</h3>
+		<div>
+		parameters -- An object containing various parameters setting up shaders and their uniforms.
+		</div>
 
 
+		<h2>Properties</h2>
 
 		<h3>.[page:object uniforms]</h3>
 		<div>

+ 0 - 9
docs/api/math/Box3.html

@@ -51,15 +51,6 @@
 		Sets the lower and upper (x, y, z) boundaries of this box.
 		</div>
 		
-		<h3>.addPoint([page:Vector3 point]) [page:Box3 this]</h3>
-		<div>
-		point -- [page:Vector3] to add to the box <br />
-		</div>
-		<div>
-		If the *point* is outside the bounds of the box, the bounds are expanded
-		so that the point is within the bounds.
-		</div>
-		
 		<h3>.applyMatrix4([page:Matrix4 matrix]) [page:Box3 this]</h3>
 		<div>
 		matrix -- The [page:Matrix4] to apply

+ 5 - 3
docs/api/objects/Line.html

@@ -22,9 +22,11 @@
 		});
 		
 		var geometry = new THREE.Geometry();
-		geometry.vertices.push( new THREE.Vector3( -10, 0, 0 ) );
-		geometry.vertices.push( new THREE.Vector3( 0, 10, 0 ) );
-		geometry.vertices.push( new THREE.Vector3( 10, 0, 0 ) );
+		geometry.vertices.push(
+			new THREE.Vector3( -10, 0, 0 ),
+			new THREE.Vector3( 0, 10, 0 ),
+			new THREE.Vector3( 10, 0, 0 )
+		);
 
 		var line = new THREE.Line( geometry, material );
 		scene.add( line );

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

@@ -15,14 +15,14 @@
 
 
 		<h2>Example</h2>
-		
-		<code>var geometry = new THREE.CubeGeometry( 1, 1, 1 );
+
+		<code>var geometry = new THREE.BoxGeometry( 1, 1, 1 );
 		var material = new THREE.MeshBasicMaterial( { color: 0xffff00 } );
 		var mesh = new THREE.Mesh( geometry, material );
 		scene.add( mesh );
 		</code>
 
-				
+
 		<h2>Constructor</h2>
 
 		<h3>[name]( [page:Geometry geometry], [page:Material material] )</h3>
@@ -53,8 +53,8 @@
 		<div>
 		Returns the index of a morph target defined by name.
 		</div>
-		
-		
+
+
 		<h3>.updateMorphTargets()</h3>
 		<div>
 		Updates the morphtargets to have no influence on the object.

+ 1 - 1
docs/api/objects/ParticleSystem.html → docs/api/objects/PointCloud.html

@@ -32,7 +32,7 @@
 
 		<h3>.[page:Material material]</h3>
 
-		<div>An instance of [page:Material], defining the object's appearance. Default is a [page:ParticleSystemMaterial] with randomised colour.</div>
+		<div>An instance of [page:Material], defining the object's appearance. Default is a [page:PointCloudMaterial] with randomised colour.</div>
 
 		<h3>.[page:Boolean frustrumCulled]</h3>
 		

+ 59 - 17
docs/api/renderers/webgl/WebGLProgram.html

@@ -9,36 +9,78 @@
 	<body>
 		<h1>[name]</h1>
 
-		<div class="desc">todo</div>
+		<div class="desc">Constructor for the GLSL program sent to vertex and fragment shaders, including default uniforms and attributes.</div>
 
-		<h2>Example</h2>
+		<h2>Constructor</h2>
 
-		<code>todo</code>
+		<h3>[name]( [page:WebGLRenderer renderer], [page:Object code], [page:Material material], [page:Object parameters] )</h3>
+		<div>For parameters see [page:WebGLRenderer WebGLRenderer]</div>
+		<div>Standard defaults for vertex shader:<br/><br/>
+		uniform mat4 modelMatrix;<br/>
+		uniform mat4 modelViewMatrix;<br/>
+		uniform mat4 projectionMatrix;<br/>
+		uniform mat4 viewMatrix;<br/>
+		uniform mat3 normalMatrix;<br/>
+		uniform vec3 cameraPosition;<br/><br/>
+		attribute vec3 position;<br/>
+		attribute vec3 normal;<br/>
+		attribute vec2 uv;<br/>
+		attribute vec2 uv2;</div>
+		
+		<div>Conditional defaults for vertex shader:<br/><br/>
+		attribute vec3 color;<br/><br/>
+		attribute vec3 morphTarget0;<br/>
+		attribute vec3 morphTarget1;<br/>
+		attribute vec3 morphTarget2;<br/>
+		attribute vec3 morphTarget3;<br/><br/>
+		attribute vec3 morphNormal0;<br/>
+		attribute vec3 morphNormal1;<br/>
+		attribute vec3 morphNormal2;<br/>
+		attribute vec3 morphNormal3;<br/><br/>
+		attribute vec3 morphTarget4;<br/>
+		attribute vec3 morphTarget5;<br/>
+		attribute vec3 morphTarget6;<br/>
+		attribute vec3 morphTarget7;<br/><br/>
+		attribute vec4 skinIndex;<br/>
+		attribute vec4 skinWeight;</div>
+		
+		<div>Standard defaults for fragment shader:<br/><br/>
+		uniform mat4 viewMatrix;<br/>
+		uniform vec3 cameraPosition;</div>
+		
 
-		<h2>Constructor</h2>
+		<h2>Properties</h2>
 
+		<h3>.[page:Object uniforms]</h3>
+		<div></div> 
 
-		<h3>todo</h3>
-		<div></div>
+		<h3>.[page:Object attributes]</h3>
+		<div></div> 
 
+		<h3>.[page:Integer id]</h3>
+		<div></div> 
 
-		<h2>Properties</h2>
+		<h3>.[page:Object code]</h3>
+		<div></div> 
+
+		<h3>.[page:Integer usedTimes]</h3>
+		<div></div> 
+
+		<h3>.[page:Object program]</h3>
+		<div></div> 
 
-		<h3>todo</h3>
-		<div>
-		todo
-		</div> 
+		<h3>.[page:Object vertexShader]</h3>
+		<div></div> 
+
+		<h3>.[page:Object fragmentShader]</h3>
+		<div></div> 
 
 
 		<h2>Methods</h2>
 		
+		<h3>none</h3>
+		<div></div>
 
-		<h3>todo</h3>
-		<div>todo</div>
-		<div>
-		todo
-		</div>
-		
 		<h2>Source</h2>
 
 		[link:https://github.com/mrdoob/three.js/blob/master/src/[path].js src/[path].js]

+ 216 - 12
docs/index.html

@@ -3,6 +3,7 @@
 	<head>
 		<meta charset="utf-8">
 		<title>three.js - documentation</title>
+		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
 		<style>
 			@font-face {
 				font-family: 'inconsolata';
@@ -11,6 +12,8 @@
 				font-style: normal;
 			}
 
+			*{ box-sizing: border-box;}
+
 			html {
 				height: 100%;
 			}
@@ -80,6 +83,110 @@
 				overflow: auto;
 			}
 
+			.filterBlock{
+				margin: 20px;
+				position: relative;
+			}
+			.filterBlock p {
+				margin: 0;
+			}
+			#filterInput {
+				width: 100%;
+				padding: 5px;
+				font-family: inherit;
+				font-size: 15px;
+				outline: none;
+				border: 1px solid #dedede;
+			}
+			#filterInput:focus{
+				border: 1px solid #2194CE;
+			}
+			#clearFilterButton {
+				position: absolute; 
+				right: 6px;
+				top: 50%;
+				margin-top: -8px;
+				width: 16px;
+				height: 16px;
+				font-size: 14px;
+				color: grey;
+				text-align: center;
+				line-height: 0;
+				padding-top: 7px;
+				opacity: .5;
+			}
+			#clearFilterButton:hover{
+				opacity: 1;
+			}
+			.filtered{
+				display: none;
+			}
+			#panel li b{
+				font-weight: bold;
+			}
+			#expandButton{
+				display: none;
+				position: absolute;
+				right: 20px;
+				top: 12px;
+				width: 32px;
+				height: 32px;
+				background-color: #2194CE;
+			}
+			#expandButton span{
+				height: 3px;
+				background-color: white;
+				width: 16px;
+				position: absolute;
+				left: 8px;
+				top: 8px;
+			}
+			#expandButton span:nth-child(1) {
+				top: 14px;
+			}
+			#expandButton span:nth-child(2) {
+				top: 20px;
+				width: 12px;
+			}
+			@media all and ( max-width: 640px ) {
+				#panel{
+					position: absolute;
+					left: 0;
+					top: 0;
+					height: 480px;
+					width: 100%;
+					right: 0;
+					z-index: 100;
+					overflow: hidden;
+					border-bottom: 1px solid #dedede;
+				}
+				#content{
+					overflow: auto;
+					position: absolute;
+					left: 0;
+					top: 120px;
+					right: 0;
+					bottom: 0;
+				}
+				#viewer{
+					position: absolute;
+					left: 0;
+					top: 48px;
+				}
+				#expandButton{
+					display: block;
+				}
+				#panel.collapsed{
+					height: 56px;
+				}
+				#panel h1{
+					margin-top: 20px;
+					margin-bottom: 20px;
+				}
+				#content{
+					top: 90px;
+				}
+			}
 		</style>
 	</head>
 	<body>
@@ -97,7 +204,18 @@
 
 		</script>
 
-		<div id="panel"></div>
+		<div id="panel" class="collapsed">
+			<a id="expandButton" href="#" >
+				<span></span><span></span><span></span>
+			</a>
+			<h1><a href="http://threejs.org">three.js</a> / docs</h1>
+			<div class="filterBlock" >
+				<input type="text" id="filterInput" placeholder="Type to filter"/>
+				<a href="#" id="clearFilterButton" >x</a>
+			</div>
+			<div id="content" >
+			</div>
+		</div>
 		<iframe id="viewer"></iframe>
 
 		<script src="list.js"></script>
@@ -105,46 +223,132 @@
 			var panel = document.getElementById( 'panel' );
 			var viewer = document.getElementById( 'viewer' );
 
-			var html = '<h1><a href="http://threejs.org">three.js</a> / docs</h1>';
+			var clearFilterButton = document.getElementById( 'clearFilterButton' );
+			var filterInput = document.getElementById( 'filterInput' );
+			//filterInput.focus();
+
+			panel.addEventListener( 'click', function( e ) {
+				//filterInput.focus();
+				e.preventDefault();
+			} );
+
+			document.getElementById( 'expandButton' ).addEventListener( 'click', function( e ) {
+				panel.classList.toggle( 'collapsed' );
+				if( !panel.classList.contains( 'collapsed' ) ) {
+					//filterInput.focus();
+				}
+				e.preventDefault();
+			} );
 
 			var DELIMITER = '/';
 			var nameCategoryMap = {};
+			var sections = [];
+
+			var content = document.getElementById( 'content' );
 
 			for ( var section in list ) {
 
-				html += '<h2>' + section + '</h2>';
+				var h2 = document.createElement( 'h2' );
+				h2.textContent = section;
 
-				html += '<ul>';
+				content.appendChild( h2 );
 
 				for ( var category in list[ section ] ) {
 
-					html += '<h3>' + category + '</h3>';
+					var div = document.createElement( 'div' );
+
+					var h3 = document.createElement( 'h3' );
+					h3.textContent = category;
 
-					html += '<ul>';
+					div.appendChild( h3 );
+
+					var ul = document.createElement( 'ul' );
+					div.appendChild( ul );
 
 					for ( var i = 0; i < list[ section ][ category ].length; i ++ ) {
 
 						var page = list[ section ][ category ][ i ];
 
-						html += '<li><a href="javascript:goTo(\'' + section + '\', \'' + category + '\', \'' + page[ 0 ] + '\')">' + page[ 0 ] + '</a></li>';
+						var li = document.createElement( 'li' );
+						var a = document.createElement( 'a' );
+						a.setAttribute( 'href', '#' );
+						( function( s, c, p ) { 
+							a.addEventListener( 'click', function( e ) {
+								goTo( s, c, p );
+								e.preventDefault();
+							} ) 
+						} )( section, category, page[ 0 ] )
+						a.textContent = page[ 0 ];
+						li.appendChild( a );
+						ul.appendChild( li );
 
 						nameCategoryMap[page[0]] = {
 							section: section,
 							category: category,
-							name: page[0]
+							name: page[0],
+							element: a
 						};
 
 					}
 
-					html += '</ul>';
+					content.appendChild( div );
+					sections.push( ul );
 
 				}
 
-				html += '</ul>';
 
 			}
 
-			panel.innerHTML += html;
+			panel.appendChild( content )
+
+			function layoutList() {
+
+				sections.forEach( function( el ) {
+					var collapsed = true;
+					Array.prototype.slice.apply( el.children ).forEach( function( item ) {
+						if( !item.classList.contains( 'filtered' ) ) {
+							collapsed = false;
+							return;
+						}
+					} );
+					if( collapsed ) {
+						el.parentElement.classList.add( 'filtered' );
+					} else {
+						el.parentElement.classList.remove( 'filtered' );
+					}
+				} );
+			}
+
+			filterInput.addEventListener( 'input', function( e ) {
+				updateFilter();
+			} );
+
+			clearFilterButton.addEventListener( 'click', function( e ) {
+				filterInput.value = '';
+				updateFilter();
+				e.preventDefault();
+			} );
+
+			function updateFilter() {
+
+				var exp = new RegExp( filterInput.value, 'gi' );
+				for( var j in nameCategoryMap ) {
+					var res = nameCategoryMap[ j ].name.match( exp );
+					if( res && res.length > 0 ) {
+						nameCategoryMap[ j ].element.parentElement.classList.remove( 'filtered' );
+						var str = nameCategoryMap[ j ].name;
+						for( var i = 0; i < res.length; i++ ) {
+							str = str.replace( res[ i ], '<b>' + res[ i ] + '</b>' );
+						}
+						nameCategoryMap[ j ].element.innerHTML = str;
+					} else {
+						nameCategoryMap[ j ].element.parentElement.classList.add( 'filtered' );
+						nameCategoryMap[ j ].element.textContent = nameCategoryMap[ j ].name;
+					}
+				}
+				layoutList();
+
+			}
 
 			function encodeUrl( path ) {
 
@@ -176,8 +380,8 @@
 				window.location.hash = url;
 				window.document.title = title;
 
-
 				viewer.src = pages[ section ][ category ][ name ] + '.html';
+				panel.classList.add( 'collapsed' );
 
 			}
 

+ 2 - 3
docs/list.js

@@ -53,7 +53,6 @@ var list = {
 			[ "LoadingManager", "api/loaders/LoadingManager" ],
 			[ "MaterialLoader", "api/loaders/MaterialLoader" ],
 			[ "ObjectLoader", "api/loaders/ObjectLoader" ],
-			[ "SceneLoader", "api/loaders/SceneLoader" ],
 			[ "TextureLoader", "api/loaders/TextureLoader" ],
 			[ "XHRLoader", "api/loaders/XHRLoader" ]
 		],
@@ -68,7 +67,7 @@ var list = {
 			[ "MeshLambertMaterial", "api/materials/MeshLambertMaterial" ],
 			[ "MeshNormalMaterial", "api/materials/MeshNormalMaterial" ],
 			[ "MeshPhongMaterial", "api/materials/MeshPhongMaterial" ],
-			[ "ParticleSystemMaterial", "api/materials/ParticleSystemMaterial" ],
+			[ "PointCloudMaterial", "api/materials/PointCloudMaterial" ],
 			[ "RawShaderMaterial", "api/materials/RawShaderMaterial" ],
 			[ "ShaderMaterial", "api/materials/ShaderMaterial" ],
 			[ "SpriteCanvasMaterial", "api/materials/SpriteCanvasMaterial" ],
@@ -102,7 +101,7 @@ var list = {
 			[ "LOD", "api/objects/LOD" ],
 			[ "Mesh", "api/objects/Mesh" ],
 			[ "MorphAnimMesh", "api/objects/MorphAnimMesh" ],
-			[ "ParticleSystem", "api/objects/ParticleSystem" ],
+			[ "PointCloud", "api/objects/PointCloud" ],
 			[ "SkinnedMesh", "api/objects/SkinnedMesh" ],
 			[ "Sprite", "api/objects/Sprite" ]
 		],

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

@@ -65,7 +65,7 @@
 		<div><em>"That's all good, but where's that cube you promised?"</em> Let's add it now.</div>
 
 		<code>
-		var geometry = new THREE.CubeGeometry(1,1,1);
+		var geometry = new THREE.BoxGeometry(1,1,1);
 		var material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );
 		var cube = new THREE.Mesh( geometry, material );
 		scene.add( cube );
@@ -73,7 +73,7 @@
 		camera.position.z = 5;
 		</code>
 
-		<div>To create a cube, we need a <strong>CubeGeometry</strong>. This is an object that contains all the points (<strong>vertices</strong>) and fill (<strong>faces</strong>) of the cube. We'll explore this more in the future.</div>
+		<div>To create a cube, we need a <strong>BoxGeometry</strong>. This is an object that contains all the points (<strong>vertices</strong>) and fill (<strong>faces</strong>) of the cube. We'll explore this more in the future.</div>
 
 		<div>In addition to the geometry, we need a material to color it. Three.js comes with several materials, but we'll stick to the <strong>MeshBasicMaterial</strong> for now. All materials take an object of properties which will be applied to them. To keep things very simple, we only supply a color attribute of <strong>0x00ff00</strong>, which is green. This works the same way that colors work in CSS or Photoshop (<strong>hex colors</strong>).</div>
 
@@ -130,7 +130,7 @@
 					renderer.setSize(window.innerWidth, window.innerHeight);
 					document.body.appendChild(renderer.domElement);
 
-					var geometry = new THREE.CubeGeometry(1,1,1);
+					var geometry = new THREE.BoxGeometry(1,1,1);
 					var material = new THREE.MeshBasicMaterial({color: 0x00ff00});
 					var cube = new THREE.Mesh(geometry, material);
 					scene.add(cube);

+ 6 - 0
editor/css/dark.css

@@ -44,6 +44,12 @@ button {
 	position: relative;
 }
 
+textarea {
+	white-space: pre;
+	word-wrap: normal;
+	overflow: scroll;
+}
+
 .Panel {
 
 	-moz-user-select: none;

+ 6 - 0
editor/css/light.css

@@ -18,6 +18,12 @@ button {
 	position: relative;
 }
 
+textarea {
+	white-space: pre;
+	word-wrap: normal;
+	overflow: scroll;
+}
+
 .Panel {
 	-moz-user-select: none;
 	-webkit-user-select: none;

+ 4 - 3
editor/index.html

@@ -32,6 +32,7 @@
 		<script src="../examples/js/loaders/ColladaLoader.js"></script>
 		<script src="../examples/js/loaders/OBJLoader.js"></script>
 		<script src="../examples/js/loaders/PLYLoader.js"></script>
+		<script src="../examples/js/loaders/SceneLoader.js"></script>
 		<script src="../examples/js/loaders/STLLoader.js"></script>
 		<script src="../examples/js/loaders/UTF8Loader.js"></script>
 		<script src="../examples/js/loaders/VRMLLoader.js"></script>
@@ -48,8 +49,6 @@
 
 		<!-- WIP -->
 
-		<script src="../examples/js/BufferGeometryUtils.js"></script>
-
 		<script src="../examples/js/exporters/BufferGeometryExporter.js"></script>
 		<script src="../examples/js/exporters/TypedGeometryExporter.js"></script>
 		<script src="../examples/js/exporters/GeometryExporter.js"></script>
@@ -77,8 +76,10 @@
 		<script src="js/Sidebar.Renderer.js"></script>
 		<script src="js/Sidebar.Scene.js"></script>
 		<script src="js/Sidebar.Object3D.js"></script>
-		<script src="js/Sidebar.Geometry.js"></script>
 		<script src="js/Sidebar.Animation.js"></script>
+		<script src="js/Sidebar.Geometry.js"></script>
+		<script src="js/Sidebar.Geometry.Geometry.js"></script>
+		<script src="js/Sidebar.Geometry.BufferGeometry.js"></script>
 		<script src="js/Sidebar.Geometry.BoxGeometry.js"></script>
 		<script src="js/Sidebar.Geometry.CircleGeometry.js"></script>
 		<script src="js/Sidebar.Geometry.CylinderGeometry.js"></script>

+ 8 - 2
editor/js/Editor.js

@@ -6,7 +6,8 @@ var Editor = function () {
 
 		// actions
 
-		playAnimations: new SIGNALS.Signal(),
+		playAnimation: new SIGNALS.Signal(),
+		stopAnimation: new SIGNALS.Signal(),
 
 		// notifications
 
@@ -197,6 +198,10 @@ Editor.prototype = {
 
 				helper = new THREE.HemisphereLightHelper( object, 10 );
 
+			} else if ( object instanceof THREE.SkinnedMesh ) {
+
+				helper = new THREE.SkeletonHelper( object );
+
 			} else {
 
 				// no helper for this object type
@@ -321,6 +326,7 @@ Editor.prototype = {
 			'HemisphereLight': THREE.HemisphereLight,
 			'PointLight': THREE.PointLight,
 			'SpotLight': THREE.SpotLight,
+			'SkinnedMesh': THREE.SkinnedMesh,
 			'Mesh': THREE.Mesh,
 			'Sprite': THREE.Sprite,
 			'Object3D': THREE.Object3D
@@ -381,7 +387,7 @@ Editor.prototype = {
 			'MeshLambertMaterial': THREE.MeshLambertMaterial,
 			'MeshNormalMaterial': THREE.MeshNormalMaterial,
 			'MeshPhongMaterial': THREE.MeshPhongMaterial,
-			'ParticleSystemMaterial': THREE.ParticleSystemMaterial,
+			'PointCloudMaterial': THREE.PointCloudMaterial,
 			'ShaderMaterial': THREE.ShaderMaterial,
 			'SpriteCanvasMaterial': THREE.SpriteCanvasMaterial,
 			'SpriteMaterial': THREE.SpriteMaterial,

+ 12 - 1
editor/js/Loader.js

@@ -339,7 +339,18 @@ var Loader = function ( editor ) {
 			geometry.sourceType = "ascii";
 			geometry.sourceFile = file.name;
 
-			var mesh = new THREE.Mesh( geometry, material );
+			var mesh;
+
+			if ( geometry.animation && geometry.animation.hierarchy ) {
+
+				mesh = new THREE.SkinnedMesh( geometry, material );
+
+			} else {
+
+				mesh = new THREE.Mesh( geometry, material );
+
+			}
+
 			mesh.name = filename;
 
 			editor.addObject( mesh );

+ 6 - 6
editor/js/Menubar.Edit.js

@@ -40,17 +40,17 @@ Menubar.Edit = function ( editor ) {
 		// convert to BufferGeometry
 		
 		var object = editor.selected;
+
 		if ( object.geometry instanceof THREE.Geometry ) {
 
 			if ( object.parent === undefined ) return; // avoid flattening the camera or scene
 
 			if ( confirm( 'Convert ' + object.name + ' to BufferGeometry?' ) === false ) return;
 
-			delete object.__webglInit; // TODO: Remove hack (WebGLRenderer refactoring)
-
-			object.geometry = THREE.BufferGeometryUtils.fromGeometry( object.geometry );
+			object.geometry = new THREE.BufferGeometry().fromGeometry( object.geometry );
 
 			editor.signals.objectChanged.dispatch( object );
+
 		}
 
 	}
@@ -63,17 +63,17 @@ Menubar.Edit = function ( editor ) {
 
 		if ( confirm( 'Flatten ' + object.name + '?' ) === false ) return;
 
-		delete object.__webglInit; // TODO: Remove hack (WebGLRenderer refactoring)
-
 		var geometry = object.geometry.clone();
 		geometry.applyMatrix( object.matrix );
 
+
 		object.geometry = geometry;
 
 		object.position.set( 0, 0, 0 );
 		object.rotation.set( 0, 0, 0 );
 		object.scale.set( 1, 1, 1 );
-
+		
+		object.geometry.buffersNeedUpdate = true;
 		editor.signals.objectChanged.dispatch( object );
 
 	}

+ 36 - 45
editor/js/Sidebar.Animation.js

@@ -5,85 +5,76 @@ Sidebar.Animation = function ( editor ) {
 	var options = {};
 	var possibleAnimations = {};
 
-	var container = new UI.Panel();
+	var container = new UI.CollapsiblePanel();
 	container.setDisplay( 'none' );
 
-	container.add( new UI.Text( 'Animation' ) );
-	container.add( new UI.Break(), new UI.Break() );
-
-	var AnimationsRow = new UI.Panel();
-	var Animations = new UI.Select().setOptions( options ).setWidth( '130px' ).setColor( '#444' ).setFontSize( '12px' );
-	AnimationsRow.add( new UI.Text( 'animations' ).setWidth( '90px' ) );
-	AnimationsRow.add( Animations );
-	container.add( AnimationsRow );
+	container.addStatic( new UI.Text( 'ANIMATION' ) );
 	container.add( new UI.Break() );
 
-	var PlayRow = new UI.Panel();
-	var playButton = new UI.Button().setLabel("Play").onClick(play);
-	PlayRow.add( playButton );
-	container.add( PlayRow );
-	container.add( new UI.Break() );
+	var animationsRow = new UI.Panel();
+	container.add( animationsRow );
 
-	function play() {
+	var animations = {};
 
-		var value = Animations.getValue();
+	signals.objectAdded.add( function ( object ) {
 
-		if ( possibleAnimations[ value ] ) {
+		object.traverse( function ( child ) {
 
-			var anims = possibleAnimations[value]
+			if ( child instanceof THREE.SkinnedMesh ) {
 
-			for ( var i = 0; i < anims.length; i ++ ) {
+				var material = child.material;
 
-				anims[ i ].play();
+				if ( material instanceof THREE.MeshFaceMaterial ) {
 
-			}
+					for ( var i = 0; i < material.materials.length; i ++ ) {
 
-			signals.playAnimations.dispatch( anims );
+						material.materials[ i ].skinning = true;
 
-		};
+					}
 
-	}
+				} else {
 
-	signals.objectAdded.add( function ( object ) {
+					child.material.skinning = true;
 
-		if ( object instanceof THREE.Mesh ) {
+				}
 
-			if ( object.geometry && object.geometry.animation ) {
+				animations[ child.id ] = new THREE.Animation( child, child.geometry.animation );
 
-				var name = object.geometry.animation.name;
-				options[name] = name
+			}
 
-				Animations.setOptions( options );
+		} );
 
-				THREE.AnimationHandler.add( object.geometry.animation );
+	} );
 
-				var animation = new THREE.Animation( object, name, THREE.AnimationHandler.CATMULLROM );
+	signals.objectSelected.add( function ( object ) {
 
-				if ( possibleAnimations[ name ] ){
+		container.setDisplay( 'none' );
 
-					possibleAnimations[ name ].push( animation );
+		if ( object instanceof THREE.SkinnedMesh ) {
 
-				} else {
+			animationsRow.clear();
 
-					possibleAnimations[ name ] = [ animation ];
+			var animation = animations[ object.id ];
 
-				}
+			var playButton = new UI.Button().setLabel( 'Play' ).onClick( function () {
 
-			}
+				animation.play();
 
-		}
+				signals.playAnimation.dispatch( animation );
 
-	} );
+			} );
+			animationsRow.add( playButton );
 
-	signals.objectSelected.add( function ( object ) {
+			var pauseButton = new UI.Button().setLabel( 'Stop' ).onClick( function () {
 
-		if ( object && object.geometry && object.geometry.animation ) {
+				animation.stop();
 
-			container.setDisplay( 'block' );
+				signals.stopAnimation.dispatch( animation );
 
-		} else {
+			} );
+			animationsRow.add( pauseButton );
 
-			container.setDisplay( 'none' );
+			container.setDisplay( 'block' );
 
 		}
 

+ 1 - 2
editor/js/Sidebar.Geometry.BoxGeometry.js

@@ -68,8 +68,6 @@ Sidebar.Geometry.BoxGeometry = function ( signals, object ) {
 
 	function update() {
 
-		delete object.__webglInit; // TODO: Remove hack (WebGLRenderer refactoring)
-
 		object.geometry.dispose();
 
 		object.geometry = new THREE.BoxGeometry(
@@ -81,6 +79,7 @@ Sidebar.Geometry.BoxGeometry = function ( signals, object ) {
 			depthSegments.getValue()
 		);
 
+		object.geometry.buffersNeedUpdate = true;
 		object.geometry.computeBoundingSphere();
 
 		signals.objectChanged.dispatch( object );

+ 62 - 0
editor/js/Sidebar.Geometry.BufferGeometry.js

@@ -0,0 +1,62 @@
+Sidebar.Geometry.BufferGeometry = function ( signals ) {
+
+	var container = new UI.Panel();
+
+	// vertices
+
+	var verticesRow = new UI.Panel();
+	var vertices = new UI.Text().setColor( '#444' ).setFontSize( '12px' );
+
+	verticesRow.add( new UI.Text( 'Vertices' ).setWidth( '90px' ) );
+	verticesRow.add( vertices );
+
+	container.add( verticesRow );
+
+	// faces
+
+	var facesRow = new UI.Panel();
+	var faces = new UI.Text().setColor( '#444' ).setFontSize( '12px' );
+
+	facesRow.add( new UI.Text( 'Faces' ).setWidth( '90px' ) );
+	facesRow.add( faces );
+
+	container.add( facesRow );
+
+	//
+
+	var update = function ( object ) {
+
+		if ( object === null ) return;
+
+		var geometry = object.geometry;
+
+		if ( geometry instanceof THREE.BufferGeometry ) { 
+
+			container.setDisplay( 'block' );
+
+			vertices.setValue( geometry.attributes.position.array.length / 3 );
+
+			if ( geometry.attributes.index !== undefined ) {
+
+				faces.setValue( geometry.attributes.index.array.length / 3 );
+
+			} else {
+
+				faces.setValue( geometry.attributes.position.array.length / 9 );
+
+			}
+
+		} else {
+
+			container.setDisplay( 'none' );
+
+		}
+
+	};
+
+	signals.objectSelected.add( update );
+	signals.objectChanged.add( update );
+
+	return container;
+
+}

+ 1 - 3
editor/js/Sidebar.Geometry.CircleGeometry.js

@@ -28,15 +28,13 @@ Sidebar.Geometry.CircleGeometry = function ( signals, object ) {
 
 	function update() {
 
-		delete object.__webglInit; // TODO: Remove hack (WebGLRenderer refactoring)
-
 		object.geometry.dispose();
 
 		object.geometry = new THREE.CircleGeometry(
 			radius.getValue(),
 			segments.getValue()
 		);
-
+		object.geometry.buffersNeedUpdate = true;
 		object.geometry.computeBoundingSphere();
 
 		signals.objectChanged.dispatch( object );

+ 1 - 2
editor/js/Sidebar.Geometry.CylinderGeometry.js

@@ -68,8 +68,6 @@ Sidebar.Geometry.CylinderGeometry = function ( signals, object ) {
 
 	function update() {
 
-		delete object.__webglInit; // TODO: Remove hack (WebGLRenderer refactoring)
-
 		object.geometry.dispose();
 
 		object.geometry = new THREE.CylinderGeometry(
@@ -81,6 +79,7 @@ Sidebar.Geometry.CylinderGeometry = function ( signals, object ) {
 			openEnded.getValue()
 		);
 
+		object.geometry.buffersNeedUpdate = true;
 		object.geometry.computeBoundingSphere();
 
 		signals.objectChanged.dispatch( object );

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

@@ -0,0 +1,53 @@
+Sidebar.Geometry.Geometry = function ( signals ) {
+
+	var container = new UI.Panel();
+
+	// vertices
+
+	var verticesRow = new UI.Panel();
+	var vertices = new UI.Text().setColor( '#444' ).setFontSize( '12px' );
+
+	verticesRow.add( new UI.Text( 'Vertices' ).setWidth( '90px' ) );
+	verticesRow.add( vertices );
+
+	container.add( verticesRow );
+
+	// faces
+
+	var facesRow = new UI.Panel();
+	var faces = new UI.Text().setColor( '#444' ).setFontSize( '12px' );
+
+	facesRow.add( new UI.Text( 'Faces' ).setWidth( '90px' ) );
+	facesRow.add( faces );
+
+	container.add( facesRow );
+
+	//
+
+	var update = function ( object ) {
+
+		if ( object === null ) return;
+
+		var geometry = object.geometry;
+
+		if ( geometry instanceof THREE.Geometry ) { 
+
+			container.setDisplay( 'block' );
+
+			vertices.setValue( geometry.vertices.length );
+			faces.setValue( geometry.faces.length );
+
+		} else {
+
+			container.setDisplay( 'none' );
+
+		}
+
+	};
+
+	signals.objectSelected.add( update );
+	signals.objectChanged.add( update );
+
+	return container;
+
+}

+ 1 - 2
editor/js/Sidebar.Geometry.IcosahedronGeometry.js

@@ -29,8 +29,6 @@ Sidebar.Geometry.IcosahedronGeometry = function ( signals, object ) {
 
 	function update() {
 
-		delete object.__webglInit; // TODO: Remove hack (WebGLRenderer refactoring)
-
 		object.geometry.dispose();
 
 		object.geometry = new THREE.IcosahedronGeometry(
@@ -38,6 +36,7 @@ Sidebar.Geometry.IcosahedronGeometry = function ( signals, object ) {
 			detail.getValue()
 		);
 
+		object.geometry.buffersNeedUpdate = true;
 		object.geometry.computeBoundingSphere();
 
 		signals.objectChanged.dispatch( object );

+ 2 - 3
editor/js/Sidebar.Geometry.PlaneGeometry.js

@@ -48,9 +48,7 @@ Sidebar.Geometry.PlaneGeometry = function ( signals, object ) {
 	//
 
 	function update() {
-
-		delete object.__webglInit; // TODO: Remove hack (WebGLRenderer refactoring)
-
+		
 		object.geometry.dispose();
 
 		object.geometry = new THREE.PlaneGeometry(
@@ -60,6 +58,7 @@ Sidebar.Geometry.PlaneGeometry = function ( signals, object ) {
 			heightSegments.getValue()
 		);
 
+		object.geometry.buffersNeedUpdate = true;
 		object.geometry.computeBoundingSphere();
 
 		signals.objectChanged.dispatch( object );

+ 1 - 2
editor/js/Sidebar.Geometry.SphereGeometry.js

@@ -79,8 +79,6 @@ Sidebar.Geometry.SphereGeometry = function ( signals, object ) {
 
 	function update() {
 
-		delete object.__webglInit; // TODO: Remove hack (WebGLRenderer refactoring)
-
 		object.geometry.dispose();
 
 		object.geometry = new THREE.SphereGeometry(
@@ -93,6 +91,7 @@ Sidebar.Geometry.SphereGeometry = function ( signals, object ) {
 			thetaLength.getValue()
 		);
 
+		object.geometry.buffersNeedUpdate = true;
 		object.geometry.computeBoundingSphere();
 
 		signals.objectChanged.dispatch( object );

+ 1 - 2
editor/js/Sidebar.Geometry.TorusGeometry.js

@@ -59,8 +59,6 @@ Sidebar.Geometry.TorusGeometry = function ( signals, object ) {
 
 	function update() {
 
-		delete object.__webglInit; // TODO: Remove hack (WebGLRenderer refactoring)
-
 		object.geometry.dispose();
 
 		object.geometry = new THREE.TorusGeometry(
@@ -71,6 +69,7 @@ Sidebar.Geometry.TorusGeometry = function ( signals, object ) {
 			arc.getValue()
 		);
 
+		object.geometry.buffersNeedUpdate = true;
 		object.geometry.computeBoundingSphere();
 
 		signals.objectChanged.dispatch( object );

+ 1 - 2
editor/js/Sidebar.Geometry.TorusKnotGeometry.js

@@ -79,8 +79,6 @@ Sidebar.Geometry.TorusKnotGeometry = function ( signals, object ) {
 
 	function update() {
 
-		delete object.__webglInit; // TODO: Remove hack (WebGLRenderer refactoring)
-
 		object.geometry.dispose();
 
 		object.geometry = new THREE.TorusKnotGeometry(
@@ -93,6 +91,7 @@ Sidebar.Geometry.TorusKnotGeometry = function ( signals, object ) {
 			heightScale.getValue()
 		);
 
+		object.geometry.buffersNeedUpdate = true;
 		object.geometry.computeBoundingSphere();
 
 		signals.objectChanged.dispatch( object );

+ 8 - 58
editor/js/Sidebar.Geometry.js

@@ -5,7 +5,8 @@ Sidebar.Geometry = function ( editor ) {
 	var container = new UI.CollapsiblePanel();
 	container.setDisplay( 'none' );
 
-	container.addStatic( new UI.Text().setValue( 'GEOMETRY' ) );
+	var geometryType = new UI.Text().setTextTransform( 'uppercase' );
+	container.addStatic( geometryType );
 	container.add( new UI.Break() );
 
 	// uuid
@@ -40,35 +41,13 @@ Sidebar.Geometry = function ( editor ) {
 
 	container.add( geometryNameRow );
 
-	// class
+	// geometry
 
-	var geometryTypeRow = new UI.Panel();
-	var geometryType = new UI.Text().setWidth( '150px' ).setColor( '#444' ).setFontSize( '12px' );
+	container.add( new Sidebar.Geometry.Geometry( signals ) );
 
-	geometryTypeRow.add( new UI.Text( 'Type' ).setWidth( '90px' ) );
-	geometryTypeRow.add( geometryType );
+	// buffergeometry
 
-	container.add( geometryTypeRow );
-
-	// vertices
-
-	var geometryVerticesRow = new UI.Panel();
-	var geometryVertices = new UI.Text().setColor( '#444' ).setFontSize( '12px' );
-
-	geometryVerticesRow.add( new UI.Text( 'Vertices' ).setWidth( '90px' ) );
-	geometryVerticesRow.add( geometryVertices );
-
-	container.add( geometryVerticesRow );
-
-	// faces
-
-	var geometryFacesRow = new UI.Panel();
-	var geometryFaces = new UI.Text().setColor( '#444' ).setFontSize( '12px' );
-
-	geometryFacesRow.add( new UI.Text( 'Faces' ).setWidth( '90px' ) );
-	geometryFacesRow.add( geometryFaces );
-
-	container.add( geometryFacesRow );
+	container.add( new Sidebar.Geometry.BufferGeometry( signals ) );
 
 	// parameters
 
@@ -89,7 +68,8 @@ Sidebar.Geometry = function ( editor ) {
 
 			geometryType.setValue( editor.getGeometryType( object.geometry ) );
 
-			updateFields( geometry );
+			geometryUUID.setValue( geometry.uuid );
+			geometryName.setValue( geometry.name );
 
 			//
 
@@ -153,36 +133,6 @@ Sidebar.Geometry = function ( editor ) {
 	signals.objectSelected.add( build );
 	signals.objectChanged.add( build );
 
-	//
-
-	function updateFields( geometry ) {
-
-		geometryUUID.setValue( geometry.uuid );
-		geometryName.setValue( geometry.name );
-
-		if ( geometry instanceof THREE.Geometry ) {
-
-			geometryVertices.setValue( geometry.vertices.length );
-			geometryFaces.setValue( geometry.faces.length );
-
-		} else if ( geometry instanceof THREE.BufferGeometry ) {
-
-			geometryVertices.setValue( geometry.attributes.position.array.length / 3 );
-
-			if ( geometry.attributes.index !== undefined ) {
-
-				geometryFaces.setValue( geometry.attributes.index.array.length / 3 );
-
-			} else {
-
-				geometryFaces.setValue( geometry.attributes.position.array.length / 9 );
-
-			}
-
-		}
-
-	}
-
 	return container;
 
 }

+ 96 - 12
editor/js/Sidebar.Material.js

@@ -12,7 +12,7 @@ Sidebar.Material = function ( editor ) {
 		'MeshLambertMaterial': THREE.MeshLambertMaterial,
 		'MeshNormalMaterial': THREE.MeshNormalMaterial,
 		'MeshPhongMaterial': THREE.MeshPhongMaterial,
-		'ParticleSystemMaterial': THREE.ParticleSystemMaterial,
+		'PointCloudMaterial': THREE.PointCloudMaterial,
 		'ShaderMaterial': THREE.ShaderMaterial,
 		'SpriteMaterial': THREE.SpriteMaterial,
 		'SpriteCanvasMaterial': THREE.SpriteCanvasMaterial,
@@ -71,6 +71,7 @@ Sidebar.Material = function ( editor ) {
 		'MeshLambertMaterial': 'MeshLambertMaterial',
 		'MeshNormalMaterial': 'MeshNormalMaterial',
 		'MeshPhongMaterial': 'MeshPhongMaterial',
+		'ShaderMaterial': 'ShaderMaterial',
 		'SpriteMaterial': 'SpriteMaterial'
 
 	} ).setWidth( '150px' ).setColor( '#444' ).setFontSize( '12px' ).onChange( update );
@@ -130,6 +131,39 @@ Sidebar.Material = function ( editor ) {
 
 	container.add( materialShininessRow );
 
+	// uniforms
+
+	var materialUniformsRow = new UI.Panel();
+	var materialUniforms = new UI.TextArea().setWidth( '150px' ).setHeight( '80px' );
+	materialUniforms.setValue( '' ).onChange( update );
+
+	materialUniformsRow.add( new UI.Text( 'Uniforms' ).setWidth( '90px' ) );
+	materialUniformsRow.add( materialUniforms );
+
+	container.add( materialUniformsRow );
+
+	// vertex shader
+
+	var materialVertexShaderRow = new UI.Panel();
+	var materialVertexShader = new UI.TextArea().setWidth( '150px' ).setHeight( '80px' );
+	materialVertexShader.setValue( 'void main() {\n\tgl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\n}' ).onChange( update );
+
+	materialVertexShaderRow.add( new UI.Text( 'Vertex Shader' ).setWidth( '90px' ) );
+	materialVertexShaderRow.add( materialVertexShader );
+
+	container.add( materialVertexShaderRow );
+
+	// fragment shader
+
+	var materialFragmentShaderRow = new UI.Panel();
+	var materialFragmentShader = new UI.TextArea().setWidth( '150px' ).setHeight( '80px' );
+	materialFragmentShader.setValue( 'void main() {\n\tgl_FragColor = vec4( 1.0, 0.0, 0.0, 1.0 );\n}' ).onChange( update );
+
+	materialFragmentShaderRow.add( new UI.Text( 'Fragment Shader' ).setWidth( '90px' ) );
+	materialFragmentShaderRow.add( materialFragmentShader );
+
+	container.add( materialFragmentShaderRow );
+
 	// vertex colors
 
 	var materialVertexColorsRow = new UI.Panel();
@@ -146,6 +180,15 @@ Sidebar.Material = function ( editor ) {
 
 	container.add( materialVertexColorsRow );
 
+	// skinning
+
+	var materialSkinningRow = new UI.Panel();
+	var materialSkinning = new UI.Checkbox( false ).onChange( update );
+
+	materialSkinningRow.add( new UI.Text( 'Skinning' ).setWidth( '90px' ) );
+	materialSkinningRow.add( materialSkinning );
+
+	container.add( materialSkinningRow );
 
 	// map
 
@@ -159,7 +202,6 @@ Sidebar.Material = function ( editor ) {
 
 	container.add( materialMapRow );
 
-
 	// light map
 
 	var materialLightMapRow = new UI.Panel();
@@ -172,7 +214,6 @@ Sidebar.Material = function ( editor ) {
 
 	container.add( materialLightMapRow );
 
-
 	// bump map
 
 	var materialBumpMapRow = new UI.Panel();
@@ -187,7 +228,6 @@ Sidebar.Material = function ( editor ) {
 
 	container.add( materialBumpMapRow );
 
-
 	// normal map
 
 	var materialNormalMapRow = new UI.Panel();
@@ -200,7 +240,6 @@ Sidebar.Material = function ( editor ) {
 
 	container.add( materialNormalMapRow );
 
-
 	// specular map
 
 	var materialSpecularMapRow = new UI.Panel();
@@ -213,7 +252,6 @@ Sidebar.Material = function ( editor ) {
 
 	container.add( materialSpecularMapRow );
 
-
 	// env map
 
 	var materialEnvMapRow = new UI.Panel();
@@ -228,7 +266,6 @@ Sidebar.Material = function ( editor ) {
 
 	container.add( materialEnvMapRow );
 
-
 	// blending
 
 	var materialBlendingRow = new UI.Panel();
@@ -248,7 +285,6 @@ Sidebar.Material = function ( editor ) {
 
 	container.add( materialBlendingRow );
 
-
 	// side
 
 	var materialSideRow = new UI.Panel();
@@ -265,7 +301,6 @@ Sidebar.Material = function ( editor ) {
 
 	container.add( materialSideRow );
 
-
 	// opacity
 
 	var materialOpacityRow = new UI.Panel();
@@ -276,7 +311,6 @@ Sidebar.Material = function ( editor ) {
 
 	container.add( materialOpacityRow );
 
-
 	// transparent
 
 	var materialTransparentRow = new UI.Panel();
@@ -287,7 +321,6 @@ Sidebar.Material = function ( editor ) {
 
 	container.add( materialTransparentRow );
 
-
 	// wireframe
 
 	var materialWireframeRow = new UI.Panel();
@@ -300,7 +333,6 @@ Sidebar.Material = function ( editor ) {
 
 	container.add( materialWireframeRow );
 
-
 	//
 
 	function update() {
@@ -360,6 +392,24 @@ Sidebar.Material = function ( editor ) {
 
 			}
 
+			if ( material.uniforms !== undefined ) {
+
+				material.uniforms = JSON.parse( materialUniforms.getValue() );
+
+			}
+
+			if ( material.vertexShader !== undefined ) {
+
+				material.vertexShader = materialVertexShader.getValue();
+
+			}
+
+			if ( material.fragmentShader !== undefined ) {
+
+				material.fragmentShader = materialFragmentShader.getValue();
+
+			}
+
 			if ( material.vertexColors !== undefined ) {
 
 				geometry.buffersNeedUpdate = true;
@@ -370,6 +420,12 @@ Sidebar.Material = function ( editor ) {
 
 			}
 
+			if ( material.skinning !== undefined ) {
+
+				material.skinning = materialSkinning.getValue();
+
+			}
+
 			if ( material.map !== undefined ) {
 
 				var mapEnabled = materialMapEnabled.getValue() === true;
@@ -557,7 +613,11 @@ Sidebar.Material = function ( editor ) {
 			'emissive': materialEmissiveRow,
 			'specular': materialSpecularRow,
 			'shininess': materialShininessRow,
+			'uniforms': materialUniformsRow,
+			'vertexShader': materialVertexShaderRow,
+			'fragmentShader': materialFragmentShaderRow,
 			'vertexColors': materialVertexColorsRow,
+			'skinning': materialSkinningRow,
 			'map': materialMapRow,
 			'lightMap': materialLightMapRow,
 			'bumpMap': materialBumpMapRow,
@@ -635,12 +695,36 @@ Sidebar.Material = function ( editor ) {
 
 			}
 
+			if ( material.uniforms !== undefined ) {
+
+				materialUniforms.setValue( JSON.stringify( material.uniforms, null, '  ' ) );
+
+			}
+
+			if ( material.vertexShader !== undefined ) {
+
+				materialVertexShader.setValue( material.vertexShader );
+
+			}
+
+			if ( material.fragmentShader !== undefined ) {
+
+				materialFragmentShader.setValue( material.fragmentShader );
+
+			}
+
 			if ( material.vertexColors !== undefined ) {
 
 				materialVertexColors.setValue( material.vertexColors );
 
 			}
 
+			if ( material.skinning !== undefined ) {
+
+				materialSkinning.setValue( material.skinning );
+
+			}
+
 			if ( material.map !== undefined ) {
 
 				materialMapEnabled.setValue( material.map !== null );

+ 67 - 71
editor/js/Sidebar.Object3D.js

@@ -372,9 +372,7 @@ Sidebar.Object3D = function ( editor ) {
 
 	}
 
-	function updateRows() {
-
-		var object = editor.selected;
+	function updateRows( object ) {
 
 		var properties = {
 			'parent': objectParentRow,
@@ -397,9 +395,7 @@ Sidebar.Object3D = function ( editor ) {
 
 	}
 
-	function updateTransformRows() {
-
-		var object = editor.selected;
+	function updateTransformRows( object ) {
 
 		if ( object instanceof THREE.Light ||
 		   ( object instanceof THREE.Object3D && object.userData.targetInverse ) ) {
@@ -420,7 +416,18 @@ Sidebar.Object3D = function ( editor ) {
 
 	signals.objectSelected.add( function ( object ) {
 
-		updateUI();
+		if ( object !== null ) {
+
+			container.setDisplay( 'block' );
+
+			updateRows( object );
+			updateUI( object );
+
+		} else {
+
+			container.setDisplay( 'none' );
+
+		}
 
 	} );
 
@@ -454,116 +461,105 @@ Sidebar.Object3D = function ( editor ) {
 
 		if ( object !== editor.selected ) return;
 
-		updateUI();
+		updateUI( object );
 
 	} );
 
-	function updateUI() {
-
-		container.setDisplay( 'none' );
-
-		var object = editor.selected;
-
-		if ( object !== null ) {
-
-			container.setDisplay( 'block' );
-
-			objectType.setValue( editor.getObjectType( object ) );
+	function updateUI( object ) {
 
-			objectUUID.setValue( object.uuid );
-			objectName.setValue( object.name );
+		objectType.setValue( editor.getObjectType( object ) );
 
-			if ( object.parent !== undefined ) {
+		objectUUID.setValue( object.uuid );
+		objectName.setValue( object.name );
 
-				objectParent.setValue( object.parent.id );
+		if ( object.parent !== undefined ) {
 
-			}
+			objectParent.setValue( object.parent.id );
 
-			objectPositionX.setValue( object.position.x );
-			objectPositionY.setValue( object.position.y );
-			objectPositionZ.setValue( object.position.z );
+		}
 
-			objectRotationX.setValue( object.rotation.x );
-			objectRotationY.setValue( object.rotation.y );
-			objectRotationZ.setValue( object.rotation.z );
+		objectPositionX.setValue( object.position.x );
+		objectPositionY.setValue( object.position.y );
+		objectPositionZ.setValue( object.position.z );
 
-			objectScaleX.setValue( object.scale.x );
-			objectScaleY.setValue( object.scale.y );
-			objectScaleZ.setValue( object.scale.z );
+		objectRotationX.setValue( object.rotation.x );
+		objectRotationY.setValue( object.rotation.y );
+		objectRotationZ.setValue( object.rotation.z );
 
-			if ( object.fov !== undefined ) {
+		objectScaleX.setValue( object.scale.x );
+		objectScaleY.setValue( object.scale.y );
+		objectScaleZ.setValue( object.scale.z );
 
-				objectFov.setValue( object.fov );
+		if ( object.fov !== undefined ) {
 
-			}
+			objectFov.setValue( object.fov );
 
-			if ( object.near !== undefined ) {
+		}
 
-				objectNear.setValue( object.near );
+		if ( object.near !== undefined ) {
 
-			}
+			objectNear.setValue( object.near );
 
-			if ( object.far !== undefined ) {
+		}
 
-				objectFar.setValue( object.far );
+		if ( object.far !== undefined ) {
 
-			}
+			objectFar.setValue( object.far );
 
-			if ( object.intensity !== undefined ) {
+		}
 
-				objectIntensity.setValue( object.intensity );
+		if ( object.intensity !== undefined ) {
 
-			}
+			objectIntensity.setValue( object.intensity );
 
-			if ( object.color !== undefined ) {
+		}
 
-				objectColor.setHexValue( object.color.getHexString() );
+		if ( object.color !== undefined ) {
 
-			}
+			objectColor.setHexValue( object.color.getHexString() );
 
-			if ( object.groundColor !== undefined ) {
+		}
 
-				objectGroundColor.setHexValue( object.groundColor.getHexString() );
+		if ( object.groundColor !== undefined ) {
 
-			}
+			objectGroundColor.setHexValue( object.groundColor.getHexString() );
 
-			if ( object.distance !== undefined ) {
+		}
 
-				objectDistance.setValue( object.distance );
+		if ( object.distance !== undefined ) {
 
-			}
+			objectDistance.setValue( object.distance );
 
-			if ( object.angle !== undefined ) {
+		}
 
-				objectAngle.setValue( object.angle );
+		if ( object.angle !== undefined ) {
 
-			}
+			objectAngle.setValue( object.angle );
 
-			if ( object.exponent !== undefined ) {
+		}
 
-				objectExponent.setValue( object.exponent );
+		if ( object.exponent !== undefined ) {
 
-			}
+			objectExponent.setValue( object.exponent );
 
-			objectVisible.setValue( object.visible );
+		}
 
-			try {
+		objectVisible.setValue( object.visible );
 
-				objectUserData.setValue( JSON.stringify( object.userData, null, '  ' ) );
+		try {
 
-			} catch ( error ) {
+			objectUserData.setValue( JSON.stringify( object.userData, null, '  ' ) );
 
-				console.log( error );
+		} catch ( error ) {
 
-			}
+			console.log( error );
 
-			objectUserData.setBorderColor( '#ccc' );
-			objectUserData.setBackgroundColor( '' );
+		}
 
-			updateRows();
-			updateTransformRows();
+		objectUserData.setBorderColor( '#ccc' );
+		objectUserData.setBackgroundColor( '' );
 
-		}
+		updateTransformRows( object );
 
 	}
 

+ 34 - 11
editor/js/Viewport.js

@@ -429,22 +429,23 @@ var Viewport = function ( editor ) {
 
 	} );
 
-	signals.playAnimations.add( function (animations) {
-		
-		function animate() {
+	var animations = [];
 
-			requestAnimationFrame( animate );
-			
-			for ( var i = 0; i < animations.length ; i ++ ) {
+	signals.playAnimation.add( function ( animation ) {
 
-				animations[i].update(0.016);
+		animations.push( animation );
 
-			} 
+	} );
 
-			render();
-		}
+	signals.stopAnimation.add( function ( animation ) {
+
+		var index = animations.indexOf( animation );
+
+		if ( index !== -1 ) {
 
-		animate();
+			animations.splice( index, 1 );
+
+		}
 
 	} );
 
@@ -563,6 +564,28 @@ var Viewport = function ( editor ) {
 
 		requestAnimationFrame( animate );
 
+		// animations
+
+		if ( THREE.AnimationHandler.animations.length > 0 ) {
+
+			THREE.AnimationHandler.update( 0.016 );
+
+			for ( var i = 0, l = sceneHelpers.children.length; i < l; i ++ ) {
+
+				var helper = sceneHelpers.children[ i ];
+
+				if ( helper instanceof THREE.SkeletonHelper ) {
+
+					helper.update();
+
+				}
+
+			}
+
+			render();
+
+		}
+
 	}
 
 	function render() {

+ 13 - 0
editor/js/libs/ui.js

@@ -353,11 +353,24 @@ UI.TextArea = function () {
 	dom.className = 'TextArea';
 	dom.style.padding = '2px';
 	dom.style.border = '1px solid #ccc';
+	dom.spellcheck = false;
 
 	dom.addEventListener( 'keydown', function ( event ) {
 
 		event.stopPropagation();
 
+		if ( event.keyCode === 9 ) {
+
+			event.preventDefault();
+
+			var cursor = dom.selectionStart;
+
+			dom.value = dom.value.substring( 0, cursor ) + '\t' + dom.value.substring( cursor );
+			dom.selectionStart = cursor + 1;
+			dom.selectionEnd = dom.selectionStart;
+
+		}
+
 	}, false );
 
 	this.dom = dom;

+ 10 - 9
examples/canvas_camera_orthographic2.html

@@ -21,7 +21,7 @@
 	<body>
 
 		<script src="../build/three.min.js"></script>
-		<script src="../src/extras/cameras/CombinedCamera.js"></script>
+		<script src="js/cameras/CombinedCamera.js"></script>
 
 		<script src="js/libs/stats.min.js"></script>
 
@@ -46,13 +46,13 @@
 					<a href="#" onclick="camera.setZoom(2);return false;">2x</a> |
 
 				<br/>
-			Views: <a href="#" onclick="camera.toTopView();return false;">Top view</a> |
-				<a href="#" onclick="camera.toBottomView();return false;">Bottom view</a> |
-				<a href="#" onclick="camera.toLeftView();return false;">Left view</a> |
-				<a href="#" onclick="camera.toRightView();return false;">Right view</a> |
-				<a href="#" onclick="camera.toFrontView();return false;">Front view</a> |
-				<a href="#" onclick="camera.toBackView();return false;">Back view</a> |
-				<a href="#" onclick="camera.rotationAutoUpdate = true;return false;">Look at Scene</a>
+			Views: <a href="#" onclick="camera.toTopView();lookAtScene=false;return false;">Top view</a> |
+				<a href="#" onclick="camera.toBottomView();lookAtScene=false;return false;">Bottom view</a> |
+				<a href="#" onclick="camera.toLeftView();lookAtScene=false;return false;">Left view</a> |
+				<a href="#" onclick="camera.toRightView();lookAtScene=false;return false;">Right view</a> |
+				<a href="#" onclick="camera.toFrontView();lookAtScene=false;return false;">Front view</a> |
+				<a href="#" onclick="camera.toBackView();lookAtScene=false;return false;">Back view</a> |
+				<a href="#" onclick="lookAtScene=true;return false;">Look at Scene</a>
 				<br/>
 			<div id="fov"></div>
 		</div>
@@ -63,6 +63,7 @@
 
 			var container, stats;
 			var camera, scene, renderer;
+			var lookAtScene = true;
 
 			init();
 			animate();
@@ -215,7 +216,7 @@
 
 				camera.position.x = Math.cos( timer ) * 200;
 				camera.position.z = Math.sin( timer ) * 200;
-				camera.lookAt( scene.position );
+				if ( lookAtScene ) camera.lookAt( scene.position );
 
 				renderer.render( scene, camera );
 

+ 144 - 0
examples/canvas_effects_stereo.html

@@ -0,0 +1,144 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<title>three.js canvas - effects - stereo</title>
+		<meta charset="utf-8">
+		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
+		<style>
+			body {
+				background:#fff;
+				padding:0;
+				margin:0;
+				font-weight: bold;
+				overflow:hidden;
+			}
+		</style>
+	</head>
+	<body>
+
+		<script src="../build/three.min.js"></script>
+		<script src="js/effects/StereoEffect.js"></script>
+
+		<script src="js/libs/stats.min.js"></script>
+
+		<script>
+
+			var container, stats;
+
+			var camera, scene, renderer;
+
+			var geometry, group;
+
+			var effect;
+
+			var mouseX = 0, mouseY = 0;
+
+			var windowHalfX = window.innerWidth / 2;
+			var windowHalfY = window.innerHeight / 2;
+
+			document.addEventListener( 'mousemove', onDocumentMouseMove, false );
+
+			init();
+			animate();
+
+			function init() {
+
+				container = document.createElement( 'div' );
+				document.body.appendChild( container );
+
+				camera = new THREE.PerspectiveCamera( 60, window.innerWidth / window.innerHeight, 1, 10000 );
+				camera.position.z = 500;
+
+				scene = new THREE.Scene();
+
+				var geometry = new THREE.BoxGeometry( 100, 100, 100 );
+				var material = new THREE.MeshNormalMaterial( { overdraw: 0.5 } );
+
+				group = new THREE.Object3D();
+
+				for ( var i = 0; i < 200; i ++ ) {
+
+					var mesh = new THREE.Mesh( geometry, material );
+					mesh.position.x = Math.random() * 2000 - 1000;
+					mesh.position.y = Math.random() * 2000 - 1000;
+					mesh.position.z = Math.random() * 2000 - 1000;
+					mesh.rotation.x = Math.random() * 2 * Math.PI;
+					mesh.rotation.y = Math.random() * 2 * Math.PI;
+					mesh.matrixAutoUpdate = false;
+					mesh.updateMatrix();
+					group.add( mesh );
+
+				}
+
+				scene.add( group );
+
+				renderer = new THREE.CanvasRenderer();
+				renderer.setClearColor( 0xffffff );
+				renderer.setSize( window.innerWidth, window.innerHeight );
+				container.appendChild( renderer.domElement );
+
+				effect = new THREE.StereoEffect( renderer );
+				effect.setSize( window.innerWidth, window.innerHeight );
+
+				stats = new Stats();
+				stats.domElement.style.position = 'absolute';
+				stats.domElement.style.top = '0px';
+				stats.domElement.style.zIndex = 100;
+				container.appendChild( stats.domElement );
+
+				//
+
+				window.addEventListener( 'resize', onWindowResize, false );
+
+			}
+
+			function onWindowResize() {
+
+				windowHalfX = window.innerWidth / 2;
+				windowHalfY = window.innerHeight / 2;
+
+				camera.aspect = window.innerWidth / window.innerHeight;
+				camera.updateProjectionMatrix();
+
+				renderer.setSize( window.innerWidth, window.innerHeight );
+				effect.setSize( window.innerWidth, window.innerHeight );
+
+			}
+
+			function onDocumentMouseMove(event) {
+
+				mouseX = ( event.clientX - windowHalfX ) * 10;
+				mouseY = ( event.clientY - windowHalfY ) * 10;
+
+			}
+
+			//
+
+			function animate() {
+
+				requestAnimationFrame( animate );
+
+				render();
+				stats.update();
+
+			}
+
+			function render() {
+
+				camera.position.x += ( mouseX - camera.position.x ) * .05;
+				camera.position.y += ( - mouseY - camera.position.y ) * .05;
+				camera.lookAt( scene.position );
+
+				var currentSeconds = Date.now();
+				group.rotation.x = Math.sin( currentSeconds * 0.0007 ) * 0.5;
+				group.rotation.y = Math.sin( currentSeconds * 0.0003 ) * 0.5;
+				group.rotation.z = Math.sin( currentSeconds * 0.0002 ) * 0.5;
+
+				effect.render( scene, camera );
+
+			}
+
+		</script>
+
+	</body>
+</html>

+ 1 - 1
examples/canvas_geometry_birds.html

@@ -359,7 +359,6 @@
 
 					bird = birds[ i ] = new THREE.Mesh( new Bird(), new THREE.MeshBasicMaterial( { color:Math.random() * 0xffffff, side: THREE.DoubleSide } ) );
 					bird.phase = Math.floor( Math.random() * 62.83 );
-					bird.position = boids[ i ].position;
 					scene.add( bird );
 
 
@@ -429,6 +428,7 @@
 					boid.run( boids );
 
 					bird = birds[ i ];
+					bird.position.copy( boids[ i ].position );
 
 					color = bird.material.color;
 					color.r = color.g = color.b = ( 500 - bird.position.z ) / 1000;

+ 2 - 2
examples/canvas_geometry_cube.html

@@ -60,7 +60,7 @@
 
 				// Cube
 
-				var geometry = new THREE.CubeGeometry( 200, 200, 200 );
+				var geometry = new THREE.BoxGeometry( 200, 200, 200 );
 
 				for ( var i = 0; i < geometry.faces.length; i += 2 ) {
 
@@ -205,4 +205,4 @@
 		</script>
 
 	</body>
-</html>
+</html>

+ 1 - 1
examples/canvas_geometry_hierarchy.html

@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <html lang="en">
 	<head>
-		<title>three.js canvas - geometry hierarchy 2</title>
+		<title>three.js canvas - geometry - hierarchy</title>
 		<meta charset="utf-8">
 		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
 		<style>

+ 4 - 2
examples/canvas_geometry_nurbs.html

@@ -108,7 +108,6 @@
 
 				var nurbsLine = new THREE.Line( nurbsGeometry, nurbsMaterial );
 				nurbsLine.position.set( 0, -100, 0 );
-				group.add( nurbsLine );
 
 				var nurbsControlPointsGeometry = new THREE.Geometry();
 				nurbsControlPointsGeometry.vertices = nurbsCurve.controlPoints;
@@ -116,7 +115,10 @@
 
 				var nurbsControlPointsLine = new THREE.Line( nurbsControlPointsGeometry, nurbsControlPointsMaterial );
 				nurbsControlPointsLine.position.copy( nurbsLine.position );
-				group.add( nurbsControlPointsLine );
+
+				group.add( nurbsLine, nurbsControlPointsLine );
+				// this also works:
+				// group.add( nurbsLine ).add( nurbsControlPointsLine );
 
 				//
 

+ 1 - 1
examples/canvas_interactive_cubes.html

@@ -131,7 +131,7 @@
 					intersects[ 0 ].object.material.color.setHex( Math.random() * 0xffffff );
 
 					var particle = new THREE.Sprite( particleMaterial );
-					particle.position = intersects[ 0 ].point;
+					particle.position.copy( intersects[ 0 ].point );
 					particle.scale.x = particle.scale.y = 16;
 					scene.add( particle );
 

+ 15 - 28
examples/canvas_lights_pointlights.html

@@ -44,7 +44,6 @@
 		<script>
 
 			var camera, scene, renderer,
-			particle1, particle2, particle2,
 			light1, light2, light3,
 			loader, mesh;
 
@@ -80,14 +79,14 @@
 
 				}
 
-				particle1 = new THREE.Sprite( new THREE.SpriteCanvasMaterial( { color: 0xff0040, program: program } ) );
-				scene.add( particle1 );
+				var sprite = new THREE.Sprite( new THREE.SpriteCanvasMaterial( { color: 0xff0040, program: program } ) );
+				light1.add( sprite );
 
-				particle2 = new THREE.Sprite( new THREE.SpriteCanvasMaterial( { color: 0x0040ff, program: program } ) );
-				scene.add( particle2 );
+				var sprite = new THREE.Sprite( new THREE.SpriteCanvasMaterial( { color: 0x0040ff, program: program } ) );
+				light2.add( sprite );
 
-				particle3 = new THREE.Sprite( new THREE.SpriteCanvasMaterial( { color: 0x80ff80, program: program } ) );
-				scene.add( particle3 );
+				var sprite = new THREE.Sprite( new THREE.SpriteCanvasMaterial( { color: 0x80ff80, program: program } ) );
+				light3.add( sprite );
 
 				loader = new THREE.JSONLoader();
 				loader.load( 'obj/WaltHeadLo.js', function ( geometry ) {
@@ -131,29 +130,17 @@
 
 				if ( mesh ) mesh.rotation.y -= 0.01;
 
-				particle1.position.x = Math.sin( time * 0.7 ) * 30;
-				particle1.position.y = Math.cos( time * 0.5 ) * 40;
-				particle1.position.z = Math.cos( time * 0.3 ) * 30;
+				light1.position.x = Math.sin( time * 0.7 ) * 30;
+				light1.position.y = Math.cos( time * 0.5 ) * 40;
+				light1.position.z = Math.cos( time * 0.3 ) * 30;
 
-				light1.position.x = particle1.position.x;
-				light1.position.y = particle1.position.y;
-				light1.position.z = particle1.position.z;
+				light2.position.x = Math.cos( time * 0.3 ) * 30;
+				light2.position.y = Math.sin( time * 0.5 ) * 40;
+				light2.position.z = Math.sin( time * 0.7 ) * 30;
 
-				particle2.position.x = Math.cos( time * 0.3 ) * 30;
-				particle2.position.y = Math.sin( time * 0.5 ) * 40;
-				particle2.position.z = Math.sin( time * 0.7 ) * 30;
-
-				light2.position.x = particle2.position.x;
-				light2.position.y = particle2.position.y;
-				light2.position.z = particle2.position.z;
-
-				particle3.position.x = Math.sin( time * 0.7 ) * 30;
-				particle3.position.y = Math.cos( time * 0.3 ) * 40;
-				particle3.position.z = Math.sin( time * 0.5 ) * 30;
-
-				light3.position.x = particle3.position.x;
-				light3.position.y = particle3.position.y;
-				light3.position.z = particle3.position.z;
+				light3.position.x = Math.sin( time * 0.7 ) * 30;
+				light3.position.y = Math.cos( time * 0.3 ) * 40;
+				light3.position.z = Math.sin( time * 0.5 ) * 30;
 
 				renderer.render( scene, camera );
 

+ 2 - 4
examples/canvas_lines_colors.html

@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <html lang="en">
 	<head>
-		<title>three.js webgl - lines - cubes - colors</title>
+		<title>three.js canvas - lines - colors</title>
 		<meta charset="utf-8">
 		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
 		<style>
@@ -40,7 +40,7 @@
 	<body>
 
 		<div id="info">
-			<a href="http://threejs.org" target="_blank">three.js</a> - color lines WebGL demo
+			<a href="http://threejs.org" target="_blank">three.js</a> - color lines
 			[<a href="http://en.wikipedia.org/wiki/Hilbert_curve">Hilbert curve</a> thanks to <a href="http://www.openprocessing.org/visuals/?visualID=15599">Thomas Diewald</a>]
 		</div>
 
@@ -140,8 +140,6 @@
 
 				renderer.setSize( window.innerWidth, window.innerHeight );
 
-				composer.reset();
-
 			}
 
 			//

+ 8 - 12
examples/canvas_materials.html

@@ -24,7 +24,7 @@
 			var container, stats;
 
 			var camera, scene, renderer, objects;
-			var particleLight, pointLight;
+			var pointLight;
 
 			init();
 			animate();
@@ -116,10 +116,6 @@
 
 				}
 
-				particleLight = new THREE.Sprite( new THREE.SpriteCanvasMaterial( { color: 0xffffff, program: program } ) );
-				particleLight.scale.x = particleLight.scale.y = 8;
-				scene.add( particleLight );
-
 				// Lights
 
 				scene.add( new THREE.AmbientLight( Math.random() * 0x202020 ) );
@@ -134,6 +130,10 @@
 				pointLight = new THREE.PointLight( 0xffffff, 1 );
 				scene.add( pointLight );
 
+				var sprite = new THREE.Sprite( new THREE.SpriteCanvasMaterial( { color: 0xffffff, program: program } ) );
+				sprite.scale.set( 8, 8, 8 );
+				pointLight.add( sprite );
+
 				renderer = new THREE.CanvasRenderer();
 				renderer.setSize( window.innerWidth, window.innerHeight );
 				container.appendChild( renderer.domElement );
@@ -211,13 +211,9 @@
 
 				}
 
-				particleLight.position.x = Math.sin( timer * 7 ) * 300;
-				particleLight.position.y = Math.cos( timer * 5 ) * 400;
-				particleLight.position.z = Math.cos( timer * 3 ) * 300;
-
-				pointLight.position.x = particleLight.position.x;
-				pointLight.position.y = particleLight.position.y;
-				pointLight.position.z = particleLight.position.z;
+				pointLight.position.x = Math.sin( timer * 7 ) * 300;
+				pointLight.position.y = Math.cos( timer * 5 ) * 400;
+				pointLight.position.z = Math.cos( timer * 3 ) * 300;
 
 				renderer.render( scene, camera );
 

+ 1 - 2
examples/canvas_particles_shapes.html

@@ -174,8 +174,7 @@
 
 				var onParticleCreated = function( p ) {
 
-					var position = p.position;
-					p.target.position = position;
+					p.target.position.copy( p.position );
 
 				};
 

+ 1 - 1
examples/css3d_molecules.html

@@ -346,7 +346,7 @@
 
 				loader.load( url, function ( geometry, geometryBonds ) {
 
-					var offset = THREE.GeometryUtils.center( geometry );
+					var offset = geometry.center();
 					geometryBonds.applyMatrix( new THREE.Matrix4().makeTranslation( offset.x, offset.y, offset.z ) );
 
 					for ( var i = 0; i < geometry.vertices.length; i ++ ) {

+ 5 - 12
examples/css3d_sprites.html

@@ -63,19 +63,12 @@
 
 				scene = new THREE.Scene();
 
-				var sprite = document.createElement( 'img' );
-				sprite.addEventListener( 'load', function ( event ) {
+				var image = document.createElement( 'img' );
+				image.addEventListener( 'load', function ( event ) {
 
-					for ( var i = 0, j = 0; i < particlesTotal; i ++, j += 3 ) {
+					for ( var i = 0; i < particlesTotal; i ++ ) {
 
-						var canvas = document.createElement( 'canvas' );
-						canvas.width = sprite.width;
-						canvas.height = sprite.height;
-
-						var context = canvas.getContext( '2d' );
-						context.drawImage( sprite, 0, 0 );
-
-						var object = new THREE.CSS3DSprite( canvas );
+						var object = new THREE.CSS3DSprite( image.cloneNode() );
 						object.position.x = Math.random() * 4000 - 2000,
 						object.position.y = Math.random() * 4000 - 2000,
 						object.position.z = Math.random() * 4000 - 2000
@@ -88,7 +81,7 @@
 					transition();
 
 				}, false );
-				sprite.src = 'textures/sprite.png';
+				image.src = 'textures/sprite.png';
 
 				// Plane
 

+ 12 - 5
examples/index.html

@@ -134,9 +134,10 @@
 				"webgl_custom_attributes_particles2",
 				"webgl_custom_attributes_particles3",
 				"webgl_effects_anaglyph",
-				"webgl_effects_crosseyed",
 				"webgl_effects_oculusrift",
 				"webgl_effects_parallaxbarrier",
+				"webgl_effects_stereo",
+				"webgl_effects_vr",
 				"webgl_geometries",
 				"webgl_geometries2",
 				"webgl_geometry_colors",
@@ -170,6 +171,8 @@
 				"webgl_interactive_cubes",
 				"webgl_interactive_cubes_gpu",
 				"webgl_interactive_draggablecubes",
+				"webgl_interactive_particles",
+				"webgl_interactive_raycasting_pointcloud",
 				"webgl_interactive_voxelpainter",
 				"webgl_kinect",
 				"webgl_lensflares",
@@ -182,6 +185,7 @@
 				"webgl_lines_sphere",
 				"webgl_lines_splines",
 				"webgl_loader_assimp2json",
+				"webgl_loader_awd",
 				"webgl_loader_collada",
 				"webgl_loader_collada_keyframe",
 				"webgl_loader_collada_skinning",
@@ -190,12 +194,12 @@
 				"webgl_loader_gltf",
 				"webgl_loader_json_blender",
 				"webgl_loader_json_objconverter",
+				"webgl_loader_msgpack",
 				"webgl_loader_obj",
 				"webgl_loader_obj_mtl",
-				"webgl_loader_pdb",	
+				"webgl_loader_pdb",
 				"webgl_loader_ply",
 				"webgl_loader_scene",
-				"webgl_loader_scene_blender",
 				"webgl_loader_stl",
 				"webgl_loader_utf8",
 				"webgl_loader_vrml",
@@ -260,13 +264,16 @@
 				"webgl_postprocessing_dof2",
 				"webgl_postprocessing_godrays",
 				"webgl_postprocessing_crossfade",
+				"webgl_postprocessing_glitch",
 				"webgl_rtt",
 				"webgl_sandbox",
 				"webgl_shader",
+				"webgl_shader_lava",
 				"webgl_shader2",
 				"webgl_shaders_ocean",
-				"webgl_shader_lava",
 				"webgl_shading_physical",
+				"webgl_shaders_sky",
+				"webgl_shaders_vector",
 				"webgl_shadowmap",
 				"webgl_shadowmap_performance",
 				"webgl_sprites",
@@ -297,7 +304,6 @@
 				"misc_controls_fly",
 				"misc_controls_oculusrift",
 				"misc_controls_orbit",
-				"misc_controls_path",
 				"misc_controls_pointerlock",
 				"misc_controls_trackball",
 				"misc_controls_transform",
@@ -312,6 +318,7 @@
 				"canvas_ascii_effect",
 				"canvas_camera_orthographic",
 				"canvas_camera_orthographic2",
+				"canvas_effects_stereo",
 				"canvas_geometry_birds",
 				"canvas_geometry_cube",
 				"canvas_geometry_earth",

+ 3 - 5
examples/js/BlendCharacter.js

@@ -8,7 +8,7 @@ THREE.BlendCharacter = function () {
 	this.weightSchedule = [];
 	this.warpSchedule = [];
 
-	this.load = function( url, onLoad ) {
+	this.load = function ( url, onLoad ) {
 
 		var scope = this;
 
@@ -24,10 +24,8 @@ THREE.BlendCharacter = function () {
 
 			for ( var i = 0; i < geometry.animations.length; ++i ) {
 
-				THREE.AnimationHandler.add( geometry.animations[ i ] );
-
 				var animName = geometry.animations[ i ].name;
-				scope.animations[ animName ] = new THREE.Animation( scope, animName );
+				scope.animations[ animName ] = new THREE.Animation( scope, geometry.animations[ i ] );
 
 			}
 
@@ -195,7 +193,7 @@ THREE.BlendCharacter = function () {
 
 			if ( this.animations[ a ].isPlaying ) {
 
-				this.animations[ a ].pause();
+				this.animations[ a ].stop();
 
 			}
 

+ 0 - 182
examples/js/BufferGeometryUtils.js

@@ -1,182 +0,0 @@
-/**
- * @author spite / http://www.clicktorelease.com/
- * @author mrdoob / http://mrdoob.com/
- */
-
-THREE.BufferGeometryUtils = {
-
-	fromGeometry: function geometryToBufferGeometry( geometry, settings ) {
-
-		if ( geometry instanceof THREE.BufferGeometry ) {
-
-			return geometry;
-
-		}
-
-		settings = settings || { 'vertexColors': THREE.NoColors };
-
-		var vertices = geometry.vertices;
-		var faces = geometry.faces;
-		var faceVertexUvs = geometry.faceVertexUvs;
-		var vertexColors = settings.vertexColors;
-		var hasFaceVertexUv = faceVertexUvs[ 0 ].length > 0;
-		var hasFaceVertexNormals = faces[ 0 ].vertexNormals.length == 3;
-
-		var bufferGeometry = new THREE.BufferGeometry();
-
-		bufferGeometry.attributes = {
-
-			position: {
-				itemSize: 3,
-				array: new Float32Array( faces.length * 3 * 3 )
-			},
-			normal: {
-				itemSize: 3,
-				array: new Float32Array( faces.length * 3 * 3 )
-			}
-
-		}
-
-		var positions = bufferGeometry.attributes.position.array;
-		var normals = bufferGeometry.attributes.normal.array;
-
-		if ( vertexColors !== THREE.NoColors ) {
-
-			bufferGeometry.attributes.color = {
-				itemSize: 3,
-				array: new Float32Array( faces.length * 3 * 3 )
-			};
-
-			var colors = bufferGeometry.attributes.color.array;
-
-		}
-
-		if ( hasFaceVertexUv === true ) {
-
-			bufferGeometry.attributes.uv = {
-				itemSize: 2,
-				array: new Float32Array( faces.length * 3 * 2 )
-			};
-
-			var uvs = bufferGeometry.attributes.uv.array;
-
-		}
-
-		for ( var i = 0, i2 = 0, i3 = 0; i < faces.length; i ++, i2 += 6, i3 += 9 ) {
-
-			var face = faces[ i ];
-
-			var a = vertices[ face.a ];
-			var b = vertices[ face.b ];
-			var c = vertices[ face.c ];
-
-			positions[ i3     ] = a.x;
-			positions[ i3 + 1 ] = a.y;
-			positions[ i3 + 2 ] = a.z;
-			
-			positions[ i3 + 3 ] = b.x;
-			positions[ i3 + 4 ] = b.y;
-			positions[ i3 + 5 ] = b.z;
-			
-			positions[ i3 + 6 ] = c.x;
-			positions[ i3 + 7 ] = c.y;
-			positions[ i3 + 8 ] = c.z;
-
-			if ( hasFaceVertexNormals === true ) {
-
-				var na = face.vertexNormals[ 0 ];
-				var nb = face.vertexNormals[ 1 ];
-				var nc = face.vertexNormals[ 2 ];
-
-				normals[ i3     ] = na.x;
-				normals[ i3 + 1 ] = na.y;
-				normals[ i3 + 2 ] = na.z;
-
-				normals[ i3 + 3 ] = nb.x;
-				normals[ i3 + 4 ] = nb.y;
-				normals[ i3 + 5 ] = nb.z;
-
-				normals[ i3 + 6 ] = nc.x;
-				normals[ i3 + 7 ] = nc.y;
-				normals[ i3 + 8 ] = nc.z;
-
-			} else {
-
-				var n = face.normal;
-
-				normals[ i3     ] = n.x;
-				normals[ i3 + 1 ] = n.y;
-				normals[ i3 + 2 ] = n.z;
-
-				normals[ i3 + 3 ] = n.x;
-				normals[ i3 + 4 ] = n.y;
-				normals[ i3 + 5 ] = n.z;
-
-				normals[ i3 + 6 ] = n.x;
-				normals[ i3 + 7 ] = n.y;
-				normals[ i3 + 8 ] = n.z;
-
-			}
-
-			if ( vertexColors === THREE.FaceColors ) {
-
-				var fc = face.color;
-
-				colors[ i3     ] = fc.r;
-				colors[ i3 + 1 ] = fc.g;
-				colors[ i3 + 2 ] = fc.b;
-
-				colors[ i3 + 3 ] = fc.r;
-				colors[ i3 + 4 ] = fc.g;
-				colors[ i3 + 5 ] = fc.b;
-
-				colors[ i3 + 6 ] = fc.r;
-				colors[ i3 + 7 ] = fc.g;
-				colors[ i3 + 8 ] = fc.b;
-
-			} else if ( vertexColors === THREE.VertexColors ) {
-
-				var vca = face.vertexColors[ 0 ];
-				var vcb = face.vertexColors[ 1 ];
-				var vcc = face.vertexColors[ 2 ];
-
-				colors[ i3     ] = vca.r;
-				colors[ i3 + 1 ] = vca.g;
-				colors[ i3 + 2 ] = vca.b;
-
-				colors[ i3 + 3 ] = vcb.r;
-				colors[ i3 + 4 ] = vcb.g;
-				colors[ i3 + 5 ] = vcb.b;
-
-				colors[ i3 + 6 ] = vcc.r;
-				colors[ i3 + 7 ] = vcc.g;
-				colors[ i3 + 8 ] = vcc.b;
-
-			}
-
-			if ( hasFaceVertexUv === true ) {
-
-				var uva = faceVertexUvs[ 0 ][ i ][ 0 ];
-				var uvb = faceVertexUvs[ 0 ][ i ][ 1 ];
-				var uvc = faceVertexUvs[ 0 ][ i ][ 2 ];
-
-				uvs[ i2     ] = uva.x;
-				uvs[ i2 + 1 ] = uva.y;
-			
-				uvs[ i2 + 2 ] = uvb.x;
-				uvs[ i2 + 3 ] = uvb.y;
-			
-				uvs[ i2 + 4 ] = uvc.x;
-				uvs[ i2 + 5 ] = uvc.y;
-
-			}
-
-		}
-
-		bufferGeometry.computeBoundingSphere();
-
-		return bufferGeometry;
-
-	}
-
-}

+ 1 - 1
examples/js/Car.js

@@ -274,7 +274,7 @@ THREE.Car = function () {
 
 				scope.wheelDiameter = bb.max.y - bb.min.y;
 
-				THREE.GeometryUtils.center( scope.wheelGeometry );
+				scope.wheelGeometry.center();
 
 			}
 

+ 260 - 0
examples/js/SkyShader.js

@@ -0,0 +1,260 @@
+/**
+ * @author zz85 / https://github.com/zz85
+ * 
+ * Based on "A Practical Analytic Model for Daylight" 
+ * aka The Preetham Model, the de facto standard analytic skydome model
+ * http://www.cs.utah.edu/~shirley/papers/sunsky/sunsky.pdf
+ * 
+ * First implemented by Simon Wallner
+ * http://www.simonwallner.at/projects/atmospheric-scattering
+ * 
+ * Improved by Martin Upitis
+ * http://blenderartists.org/forum/showthread.php?245954-preethams-sky-impementation-HDR
+ * 
+ * Three.js integration by zz85 http://twitter.com/blurspline
+*/
+
+THREE.ShaderLib['sky'] = {
+
+	uniforms: {
+
+		luminance:	 { type: "f", value:1 },
+		turbidity:	 { type: "f", value:2 },
+		reileigh:	 { type: "f", value:1 },
+		mieCoefficient:	 { type: "f", value:0.005 },
+		mieDirectionalG: { type: "f", value:0.8 },
+		sunPosition: 	 { type: "v3", value: new THREE.Vector3() }
+
+	},
+
+	vertexShader: [
+
+		"varying vec3 vWorldPosition;",
+		"varying vec2 vUv;",
+
+		"void main() {",
+
+			"vec4 worldPosition = modelMatrix * vec4( position, 1.0 );",
+			"vWorldPosition = worldPosition.xyz;",
+			"vUv = uv;",
+
+			"gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
+
+		"}",
+
+	].join("\n"),
+
+	fragmentShader: [
+
+
+		"uniform sampler2D skySampler;",
+		"uniform vec3 sunPosition;",
+		"varying vec3 vWorldPosition;",
+		"varying vec2 vUv;",
+
+
+		"vec3 cameraPos = vec3(0., 0., 0.);",
+		"// uniform sampler2D sDiffuse;",
+		"// const float turbidity = 10.0; //",
+		"// const float reileigh = 2.; //",
+		"// const float luminance = 1.0; //",
+		"// const float mieCoefficient = 0.005;",
+		"// const float mieDirectionalG = 0.8;",
+
+		"uniform float luminance;",
+		"uniform float turbidity;",
+		"uniform float reileigh;",
+		"uniform float mieCoefficient;",
+		"uniform float mieDirectionalG;",
+
+
+		"vec3 sunDirection = normalize(sunPosition);",
+		"float reileighCoefficient = reileigh;",
+
+		"// constants for atmospheric scattering",
+		"const float e = 2.71828182845904523536028747135266249775724709369995957;",
+		"const float pi = 3.141592653589793238462643383279502884197169;",
+
+		"const float n = 1.0003; // refractive index of air",
+		"const float N = 2.545E25; // number of molecules per unit volume for air at",
+								"// 288.15K and 1013mb (sea level -45 celsius)",
+		"const float pn = 0.035;	// depolatization factor for standard air",
+
+		"// wavelength of used primaries, according to preetham",
+		"const vec3 lambda = vec3(680E-9, 550E-9, 450E-9);",
+
+		"// mie stuff",
+		"// K coefficient for the primaries",
+		"const vec3 K = vec3(0.686, 0.678, 0.666);",
+		"const float v = 4.0;",
+
+		"// optical length at zenith for molecules",
+		"const float rayleighZenithLength = 8.4E3;",
+		"const float mieZenithLength = 1.25E3;",
+		"const vec3 up = vec3(0.0, 1.0, 0.0);",
+
+		"const float EE = 1000.0;",
+		"const float sunAngularDiameterCos = 0.999956676946448443553574619906976478926848692873900859324;",
+		"// 66 arc seconds -> degrees, and the cosine of that",
+
+		"// earth shadow hack",
+		"const float cutoffAngle = pi/1.95;",
+		"const float steepness = 1.5;",
+
+
+		"vec3 totalRayleigh(vec3 lambda)",
+		"{",
+			"return (8.0 * pow(pi, 3.0) * pow(pow(n, 2.0) - 1.0, 2.0) * (6.0 + 3.0 * pn)) / (3.0 * N * pow(lambda, vec3(4.0)) * (6.0 - 7.0 * pn));",
+		"}",
+
+		"float rayleighPhase(float cosTheta)",
+		"{	 ",
+			"return (3.0 / (16.0*pi)) * (1.0 + pow(cosTheta, 2.0));",
+		"//	return (1.0 / (3.0*pi)) * (1.0 + pow(cosTheta, 2.0));",
+		"//	return (3.0 / 4.0) * (1.0 + pow(cosTheta, 2.0));",
+		"}",
+
+		"vec3 totalMie(vec3 lambda, vec3 K, float T)",
+		"{",
+			"float c = (0.2 * T ) * 10E-18;",
+			"return 0.434 * c * pi * pow((2.0 * pi) / lambda, vec3(v - 2.0)) * K;",
+		"}",
+
+		"float hgPhase(float cosTheta, float g)",
+		"{",
+			"return (1.0 / (4.0*pi)) * ((1.0 - pow(g, 2.0)) / pow(1.0 - 2.0*g*cosTheta + pow(g, 2.0), 1.5));",
+		"}",
+
+		"float sunIntensity(float zenithAngleCos)",
+		"{",
+			"return EE * max(0.0, 1.0 - exp(-((cutoffAngle - acos(zenithAngleCos))/steepness)));",
+		"}",
+
+		"// float logLuminance(vec3 c)",
+		"// {",
+		"// 	return log(c.r * 0.2126 + c.g * 0.7152 + c.b * 0.0722);",
+		"// }",
+
+		"// Filmic ToneMapping http://filmicgames.com/archives/75",
+		"float A = 0.15;",
+		"float B = 0.50;",
+		"float C = 0.10;",
+		"float D = 0.20;",
+		"float E = 0.02;",
+		"float F = 0.30;",
+		"float W = 1000.0;",
+
+		"vec3 Uncharted2Tonemap(vec3 x)",
+		"{",
+		   "return ((x*(A*x+C*B)+D*E)/(x*(A*x+B)+D*F))-E/F;",
+		"}",
+
+
+		"void main() ",
+		"{",
+			"float sunfade = 1.0-clamp(1.0-exp((sunPosition.y/450000.0)),0.0,1.0);",
+
+			"// luminance =  1.0 ;// vWorldPosition.y / 450000. + 0.5; //sunPosition.y / 450000. * 1. + 0.5;",
+
+			 "// gl_FragColor = vec4(sunfade, sunfade, sunfade, 1.0);",
+			
+			"reileighCoefficient = reileighCoefficient - (1.0* (1.0-sunfade));",
+			
+			"float sunE = sunIntensity(dot(sunDirection, up));",
+
+			"// extinction (absorbtion + out scattering) ",
+			"// rayleigh coefficients",
+			"vec3 betaR = totalRayleigh(lambda) * reileighCoefficient;",
+
+			"// mie coefficients",
+			"vec3 betaM = totalMie(lambda, K, turbidity) * mieCoefficient;",
+
+			"// optical length",
+			"// cutoff angle at 90 to avoid singularity in next formula.",
+			"float zenithAngle = acos(max(0.0, dot(up, normalize(vWorldPosition - cameraPos))));",
+			"float sR = rayleighZenithLength / (cos(zenithAngle) + 0.15 * pow(93.885 - ((zenithAngle * 180.0) / pi), -1.253));",
+			"float sM = mieZenithLength / (cos(zenithAngle) + 0.15 * pow(93.885 - ((zenithAngle * 180.0) / pi), -1.253));",
+
+
+
+			"// combined extinction factor	",
+			"vec3 Fex = exp(-(betaR * sR + betaM * sM));",
+
+			"// in scattering",
+			"float cosTheta = dot(normalize(vWorldPosition - cameraPos), sunDirection);",
+
+			"float rPhase = rayleighPhase(cosTheta*0.5+0.5);",
+			"vec3 betaRTheta = betaR * rPhase;",
+
+			"float mPhase = hgPhase(cosTheta, mieDirectionalG);",
+			"vec3 betaMTheta = betaM * mPhase;",
+
+
+			"vec3 Lin = pow(sunE * ((betaRTheta + betaMTheta) / (betaR + betaM)) * (1.0 - Fex),vec3(1.5));",
+			"Lin *= mix(vec3(1.0),pow(sunE * ((betaRTheta + betaMTheta) / (betaR + betaM)) * Fex,vec3(1.0/2.0)),clamp(pow(1.0-dot(up, sunDirection),5.0),0.0,1.0));",
+
+			"//nightsky",
+			"vec3 direction = normalize(vWorldPosition - cameraPos);",
+			"float theta = acos(direction.y); // elevation --> y-axis, [-pi/2, pi/2]",
+			"float phi = atan(direction.z, direction.x); // azimuth --> x-axis [-pi/2, pi/2]",
+			"vec2 uv = vec2(phi, theta) / vec2(2.0*pi, pi) + vec2(0.5, 0.0);",
+			"// vec3 L0 = texture2D(skySampler, uv).rgb+0.1 * Fex;",
+			"vec3 L0 = vec3(0.1) * Fex;",
+			
+			"// composition + solar disc",
+			"//if (cosTheta > sunAngularDiameterCos)",
+			"float sundisk = smoothstep(sunAngularDiameterCos,sunAngularDiameterCos+0.00002,cosTheta);",
+			"// if (normalize(vWorldPosition - cameraPos).y>0.0)",
+			"L0 += (sunE * 19000.0 * Fex)*sundisk;",
+
+
+			"vec3 whiteScale = 1.0/Uncharted2Tonemap(vec3(W));",
+			
+			"vec3 texColor = (Lin+L0);   ",
+			"texColor *= 0.04 ;",
+			"texColor += vec3(0.0,0.001,0.0025)*0.3;",
+			
+			"float g_fMaxLuminance = 1.0;",
+			"float fLumScaled = 0.1 / luminance;     ",
+			"float fLumCompressed = (fLumScaled * (1.0 + (fLumScaled / (g_fMaxLuminance * g_fMaxLuminance)))) / (1.0 + fLumScaled); ",
+
+			"float ExposureBias = fLumCompressed;",
+		   
+			"vec3 curr = Uncharted2Tonemap((log2(2.0/pow(luminance,4.0)))*texColor);",
+			"vec3 color = curr*whiteScale;",
+
+			"vec3 retColor = pow(color,vec3(1.0/(1.2+(1.2*sunfade))));",
+
+			
+			"gl_FragColor.rgb = retColor;",
+				
+			"gl_FragColor.a = 1.0;",
+		"}",
+
+	].join("\n")
+
+};
+
+THREE.Sky = function () {
+
+	var skyShader = THREE.ShaderLib[ "sky" ];
+	var skyUniforms = THREE.UniformsUtils.clone( skyShader.uniforms );
+
+	var skyMat = new THREE.ShaderMaterial( { 
+		fragmentShader: skyShader.fragmentShader, 
+		vertexShader: skyShader.vertexShader, 
+		uniforms: skyUniforms,
+		side: THREE.BackSide
+	} );
+
+	var skyGeo = new THREE.SphereGeometry( 450000, 32, 15 );
+	var skyMesh = new THREE.Mesh( skyGeo, skyMat );
+
+
+	// Expose variables
+	this.mesh = skyMesh;
+	this.uniforms = skyUniforms;
+
+
+};
+

+ 2 - 2
examples/js/UCSCharacter.js

@@ -41,7 +41,7 @@ THREE.UCSCharacter = function() {
 			geometry.computeBoundingBox();
 			geometry.computeVertexNormals();
 
-			THREE.AnimationHandler.add( geometry.animation );
+			//THREE.AnimationHandler.add( geometry.animation );
 
 			mesh = new THREE.SkinnedMesh( geometry, new THREE.MeshFaceMaterial() );
 			scope.root.add( mesh );
@@ -53,7 +53,7 @@ THREE.UCSCharacter = function() {
 			mesh.castShadow = true;
 			mesh.receiveShadow = true;
 
-			animation = new THREE.Animation( mesh, geometry.animation.name );
+			animation = new THREE.Animation( mesh, geometry.animation );
 			animation.play();
 			
 			scope.setSkin(0);

+ 236 - 0
examples/js/cameras/CombinedCamera.js

@@ -0,0 +1,236 @@
+/**
+ *	@author zz85 / http://twitter.com/blurspline / http://www.lab4games.net/zz85/blog
+ *
+ *	A general perpose camera, for setting FOV, Lens Focal Length,
+ *		and switching between perspective and orthographic views easily.
+ *		Use this only if you do not wish to manage
+ *		both a Orthographic and Perspective Camera
+ *
+ */
+
+
+THREE.CombinedCamera = function ( width, height, fov, near, far, orthoNear, orthoFar ) {
+
+	THREE.Camera.call( this );
+
+	this.fov = fov;
+
+	this.left = -width / 2;
+	this.right = width / 2
+	this.top = height / 2;
+	this.bottom = -height / 2;
+
+	// We could also handle the projectionMatrix internally, but just wanted to test nested camera objects
+
+	this.cameraO = new THREE.OrthographicCamera( width / - 2, width / 2, height / 2, height / - 2, 	orthoNear, orthoFar );
+	this.cameraP = new THREE.PerspectiveCamera( fov, width / height, near, far );
+
+	this.zoom = 1;
+
+	this.toPerspective();
+
+	var aspect = width/height;
+
+};
+
+THREE.CombinedCamera.prototype = Object.create( THREE.Camera.prototype );
+
+THREE.CombinedCamera.prototype.toPerspective = function () {
+
+	// Switches to the Perspective Camera
+
+	this.near = this.cameraP.near;
+	this.far = this.cameraP.far;
+
+	this.cameraP.fov =  this.fov / this.zoom ;
+
+	this.cameraP.updateProjectionMatrix();
+
+	this.projectionMatrix = this.cameraP.projectionMatrix;
+
+	this.inPerspectiveMode = true;
+	this.inOrthographicMode = false;
+
+};
+
+THREE.CombinedCamera.prototype.toOrthographic = function () {
+
+	// Switches to the Orthographic camera estimating viewport from Perspective
+
+	var fov = this.fov;
+	var aspect = this.cameraP.aspect;
+	var near = this.cameraP.near;
+	var far = this.cameraP.far;
+
+	// The size that we set is the mid plane of the viewing frustum
+
+	var hyperfocus = ( near + far ) / 2;
+
+	var halfHeight = Math.tan( fov / 2 ) * hyperfocus;
+	var planeHeight = 2 * halfHeight;
+	var planeWidth = planeHeight * aspect;
+	var halfWidth = planeWidth / 2;
+
+	halfHeight /= this.zoom;
+	halfWidth /= this.zoom;
+
+	this.cameraO.left = -halfWidth;
+	this.cameraO.right = halfWidth;
+	this.cameraO.top = halfHeight;
+	this.cameraO.bottom = -halfHeight;
+
+	// this.cameraO.left = -farHalfWidth;
+	// this.cameraO.right = farHalfWidth;
+	// this.cameraO.top = farHalfHeight;
+	// this.cameraO.bottom = -farHalfHeight;
+
+	// this.cameraO.left = this.left / this.zoom;
+	// this.cameraO.right = this.right / this.zoom;
+	// this.cameraO.top = this.top / this.zoom;
+	// this.cameraO.bottom = this.bottom / this.zoom;
+
+	this.cameraO.updateProjectionMatrix();
+
+	this.near = this.cameraO.near;
+	this.far = this.cameraO.far;
+	this.projectionMatrix = this.cameraO.projectionMatrix;
+
+	this.inPerspectiveMode = false;
+	this.inOrthographicMode = true;
+
+};
+
+
+THREE.CombinedCamera.prototype.setSize = function( width, height ) {
+
+	this.cameraP.aspect = width / height;
+	this.left = -width / 2;
+	this.right = width / 2
+	this.top = height / 2;
+	this.bottom = -height / 2;
+
+};
+
+
+THREE.CombinedCamera.prototype.setFov = function( fov ) {
+
+	this.fov = fov;
+
+	if ( this.inPerspectiveMode ) {
+
+		this.toPerspective();
+
+	} else {
+
+		this.toOrthographic();
+
+	}
+
+};
+
+// For mantaining similar API with PerspectiveCamera
+
+THREE.CombinedCamera.prototype.updateProjectionMatrix = function() {
+
+	if ( this.inPerspectiveMode ) {
+
+		this.toPerspective();
+
+	} else {
+
+		this.toPerspective();
+		this.toOrthographic();
+
+	}
+
+};
+
+/*
+* Uses Focal Length (in mm) to estimate and set FOV
+* 35mm (fullframe) camera is used if frame size is not specified;
+* Formula based on http://www.bobatkins.com/photography/technical/field_of_view.html
+*/
+THREE.CombinedCamera.prototype.setLens = function ( focalLength, frameHeight ) {
+
+	if ( frameHeight === undefined ) frameHeight = 24;
+
+	var fov = 2 * THREE.Math.radToDeg( Math.atan( frameHeight / ( focalLength * 2 ) ) );
+
+	this.setFov( fov );
+
+	return fov;
+};
+
+
+THREE.CombinedCamera.prototype.setZoom = function( zoom ) {
+
+	this.zoom = zoom;
+
+	if ( this.inPerspectiveMode ) {
+
+		this.toPerspective();
+
+	} else {
+
+		this.toOrthographic();
+
+	}
+
+};
+
+THREE.CombinedCamera.prototype.toFrontView = function() {
+
+	this.rotation.x = 0;
+	this.rotation.y = 0;
+	this.rotation.z = 0;
+
+	// should we be modifing the matrix instead?
+
+	this.rotationAutoUpdate = false;
+
+};
+
+THREE.CombinedCamera.prototype.toBackView = function() {
+
+	this.rotation.x = 0;
+	this.rotation.y = Math.PI;
+	this.rotation.z = 0;
+	this.rotationAutoUpdate = false;
+
+};
+
+THREE.CombinedCamera.prototype.toLeftView = function() {
+
+	this.rotation.x = 0;
+	this.rotation.y = - Math.PI / 2;
+	this.rotation.z = 0;
+	this.rotationAutoUpdate = false;
+
+};
+
+THREE.CombinedCamera.prototype.toRightView = function() {
+
+	this.rotation.x = 0;
+	this.rotation.y = Math.PI / 2;
+	this.rotation.z = 0;
+	this.rotationAutoUpdate = false;
+
+};
+
+THREE.CombinedCamera.prototype.toTopView = function() {
+
+	this.rotation.x = - Math.PI / 2;
+	this.rotation.y = 0;
+	this.rotation.z = 0;
+	this.rotationAutoUpdate = false;
+
+};
+
+THREE.CombinedCamera.prototype.toBottomView = function() {
+
+	this.rotation.x = Math.PI / 2;
+	this.rotation.y = 0;
+	this.rotation.z = 0;
+	this.rotationAutoUpdate = false;
+
+};

+ 34 - 48
examples/js/controls/DeviceOrientationControls.js

@@ -7,6 +7,8 @@
 
 THREE.DeviceOrientationControls = function ( object ) {
 
+	var scope = this;
+
 	this.object = object;
 
 	this.object.rotation.reorder( "YXZ" );
@@ -17,91 +19,75 @@ THREE.DeviceOrientationControls = function ( object ) {
 
 	this.screenOrientation = 0;
 
-	this.onDeviceOrientationChangeEvent = function( rawEvtData ) {
+	var onDeviceOrientationChangeEvent = function ( event ) {
 
-		this.deviceOrientation = rawEvtData;
+		scope.deviceOrientation = event;
 
 	};
 
-	this.onScreenOrientationChangeEvent = function() {
+	var onScreenOrientationChangeEvent = function () {
 
-		this.screenOrientation = window.orientation || 0;
+		scope.screenOrientation = window.orientation || 0;
 
 	};
 
-	this.update = function() {
+	// The angles alpha, beta and gamma form a set of intrinsic Tait-Bryan angles of type Z-X'-Y''
 
-		var alpha, beta, gamma;
+	var setObjectQuaternion = function () {
 
-		return function () {
+		var zee = new THREE.Vector3( 0, 0, 1 );
 
-			if ( this.freeze ) return;
+		var euler = new THREE.Euler();
 
-			alpha  = this.deviceOrientation.gamma ? THREE.Math.degToRad( this.deviceOrientation.alpha ) : 0; // Z
-			beta   = this.deviceOrientation.beta  ? THREE.Math.degToRad( this.deviceOrientation.beta  ) : 0; // X'
-			gamma  = this.deviceOrientation.gamma ? THREE.Math.degToRad( this.deviceOrientation.gamma ) : 0; // Y''
-			orient = this.screenOrientation       ? THREE.Math.degToRad( this.screenOrientation       ) : 0; // O
+		var q0 = new THREE.Quaternion();
 
-			setObjectQuaternion( this.object.quaternion, alpha, beta, gamma, orient );
+		var q1 = new THREE.Quaternion( - Math.sqrt( 0.5 ), 0, 0, Math.sqrt( 0.5 ) ); // - PI/2 around the x-axis
 
-		}
+		return function ( quaternion, alpha, beta, gamma, orient ) {
 
-	}();
+			euler.set( beta, alpha, - gamma, 'YXZ' );                       // 'ZXY' for the device, but 'YXZ' for us
 
-	function bind( scope, fn ) {
+			quaternion.setFromEuler( euler );                               // orient the device
 
-		return function () {
+			quaternion.multiply( q1 );                                      // camera looks out the back of the device, not the top
 
-			fn.apply( scope, arguments );
+			quaternion.multiply( q0.setFromAxisAngle( zee, - orient ) );    // adjust for screen orientation
 
-		};
+		}
 
-	};
+	}();
 
 	this.connect = function() {
 
-		this.onScreenOrientationChangeEvent(); // run once on load
+		onScreenOrientationChangeEvent(); // run once on load
 
-		window.addEventListener( 'orientationchange', bind( this, this.onScreenOrientationChangeEvent ), false );
-		window.addEventListener( 'deviceorientation', bind( this, this.onDeviceOrientationChangeEvent ), false );
+		window.addEventListener( 'orientationchange', onScreenOrientationChangeEvent, false );
+		window.addEventListener( 'deviceorientation', onDeviceOrientationChangeEvent, false );
 
-		this.freeze = false;
+		scope.freeze = false;
 
 	};
 
 	this.disconnect = function() {
 
-		this.freeze = true;
+		scope.freeze = true;
 
-		window.removeEventListener( 'orientationchange', bind( this, this.onScreenOrientationChangeEvent ), false );
-		window.removeEventListener( 'deviceorientation', bind( this, this.onDeviceOrientationChangeEvent ), false );
+		window.removeEventListener( 'orientationchange', onScreenOrientationChangeEvent, false );
+		window.removeEventListener( 'deviceorientation', onDeviceOrientationChangeEvent, false );
 
 	};
 
-	// The angles alpha, beta and gamma form a set of intrinsic Tait-Bryan angles of type Z-X'-Y''
+	this.update = function () {
 
-	setObjectQuaternion = function () {
+		if ( scope.freeze ) return;
 
-		var zee = new THREE.Vector3( 0, 0, 1 );
+		var alpha  = scope.deviceOrientation.gamma ? THREE.Math.degToRad( scope.deviceOrientation.alpha ) : 0; // Z
+		var beta   = scope.deviceOrientation.beta  ? THREE.Math.degToRad( scope.deviceOrientation.beta  ) : 0; // X'
+		var gamma  = scope.deviceOrientation.gamma ? THREE.Math.degToRad( scope.deviceOrientation.gamma ) : 0; // Y''
+		var orient = scope.screenOrientation       ? THREE.Math.degToRad( scope.screenOrientation       ) : 0; // O
 
-		var euler = new THREE.Euler();
+		setObjectQuaternion( scope.object.quaternion, alpha, beta, gamma, orient );
 
-		var q0 = new THREE.Quaternion();
-
-		var q1 = new THREE.Quaternion( - Math.sqrt( 0.5 ), 0, 0, Math.sqrt( 0.5 ) ); // - PI/2 around the x-axis
-
-		return function ( quaternion, alpha, beta, gamma, orient ) {
-
-			euler.set( beta, alpha, - gamma, 'YXZ' );                       // 'ZXY' for the device, but 'YXZ' for us
-
-			quaternion.setFromEuler( euler );                               // orient the device
-
-			quaternion.multiply( q1 );                                      // camera looks out the back of the device, not the top
-
-			quaternion.multiply( q0.setFromAxisAngle( zee, - orient ) );    // adjust for screen orientation
-
-		}
-
-	}();
+	};
 
 };

+ 12 - 5
examples/js/controls/OrbitControls.js

@@ -98,6 +98,7 @@ THREE.OrbitControls = function ( object, domElement ) {
 	var pan = new THREE.Vector3();
 
 	var lastPosition = new THREE.Vector3();
+	var lastQuaternion = new THREE.Quaternion();
 
 	var STATE = { NONE : -1, ROTATE : 0, DOLLY : 1, PAN : 2, TOUCH_ROTATE : 3, TOUCH_DOLLY : 4, TOUCH_PAN : 5 };
 
@@ -284,11 +285,17 @@ THREE.OrbitControls = function ( object, domElement ) {
 		scale = 1;
 		pan.set( 0, 0, 0 );
 
-		if ( lastPosition.distanceToSquared( this.object.position ) > EPS ) {
+		// update condition is:
+		// min(camera displacement, camera rotation in radians)^2 > EPS
+		// using small-angle approximation cos(x/2) = 1 - x^2 / 8
+
+		if ( lastPosition.distanceToSquared( this.object.position ) > EPS
+		    || 8 * (1 - lastQuaternion.dot(this.object.quaternion)) > EPS ) {
 
 			this.dispatchEvent( changeEvent );
 
 			lastPosition.copy( this.object.position );
+			lastQuaternion.copy (this.object.quaternion );
 
 		}
 
@@ -346,8 +353,8 @@ THREE.OrbitControls = function ( object, domElement ) {
 
 		}
 
-		scope.domElement.addEventListener( 'mousemove', onMouseMove, false );
-		scope.domElement.addEventListener( 'mouseup', onMouseUp, false );
+		document.addEventListener( 'mousemove', onMouseMove, false );
+		document.addEventListener( 'mouseup', onMouseUp, false );
 		scope.dispatchEvent( startEvent );
 
 	}
@@ -415,8 +422,8 @@ THREE.OrbitControls = function ( object, domElement ) {
 
 		if ( scope.enabled === false ) return;
 
-		scope.domElement.removeEventListener( 'mousemove', onMouseMove, false );
-		scope.domElement.removeEventListener( 'mouseup', onMouseUp, false );
+		document.removeEventListener( 'mousemove', onMouseMove, false );
+		document.removeEventListener( 'mouseup', onMouseUp, false );
 		scope.dispatchEvent( endEvent );
 		state = STATE.NONE;
 

+ 535 - 0
examples/js/controls/OrthographicTrackballControls.js

@@ -0,0 +1,535 @@
+/**
+ * @author Eberhard Graether / http://egraether.com/
+ * @author Patrick Fuller / http://patrick-fuller.com
+ */
+
+THREE.OrthographicTrackballControls = function ( object, domElement ) {
+
+	var _this = this;
+	var STATE = { NONE: -1, ROTATE: 0, ZOOM: 1, PAN: 2, TOUCH_ROTATE: 3, TOUCH_ZOOM: 4, TOUCH_PAN: 5 };
+
+	this.object = object;
+	this.domElement = ( domElement !== undefined ) ? domElement : document;
+
+	// API
+
+	this.enabled = true;
+
+	this.screen = { width: 0, height: 0, offsetLeft: 0, offsetTop: 0 };
+	this.radius = ( this.screen.width + this.screen.height ) / 4;
+
+	this.rotateSpeed = 1.0;
+	this.zoomSpeed = 1.2;
+	this.panSpeed = 0.3;
+
+	this.noRotate = false;
+	this.noZoom = false;
+	this.noPan = false;
+
+	this.staticMoving = false;
+	this.dynamicDampingFactor = 0.2;
+
+	this.keys = [ 65 /*A*/, 83 /*S*/, 68 /*D*/ ];
+
+	// internals
+
+	this.target = new THREE.Vector3();
+
+	var lastPosition = new THREE.Vector3();
+
+	var _state = STATE.NONE,
+	_prevState = STATE.NONE,
+
+	_eye = new THREE.Vector3(),
+
+	_rotateStart = new THREE.Vector3(),
+	_rotateEnd = new THREE.Vector3(),
+
+	_zoomStart = new THREE.Vector2(),
+	_zoomEnd = new THREE.Vector2(),
+	_zoomFactor = 1,
+
+	_touchZoomDistanceStart = 0,
+	_touchZoomDistanceEnd = 0,
+
+	_panStart = new THREE.Vector2(),
+	_panEnd = new THREE.Vector2();
+
+	// for reset
+
+	this.target0 = this.target.clone();
+	this.position0 = this.object.position.clone();
+	this.up0 = this.object.up.clone();
+
+	this.left0 = this.object.left;
+	this.right0 = this.object.right;
+	this.top0 = this.object.top;
+	this.bottom0 = this.object.bottom;
+	this.center0 = new THREE.Vector2((this.left0 + this.right0) / 2.0, (this.top0 + this.bottom0) / 2.0);
+
+	// events
+
+	var changeEvent = { type: 'change' };
+
+
+	// methods
+
+	this.handleResize = function () {
+
+		this.screen.width = window.innerWidth;
+		this.screen.height = window.innerHeight;
+
+		this.screen.offsetLeft = 0;
+		this.screen.offsetTop = 0;
+
+		this.radius = ( this.screen.width + this.screen.height ) / 4;
+
+	};
+
+	this.handleEvent = function ( event ) {
+
+		if ( typeof this[ event.type ] == 'function' ) {
+
+			this[ event.type ]( event );
+
+		}
+
+	};
+
+	this.getMouseOnScreen = function ( clientX, clientY ) {
+
+		return new THREE.Vector2(
+			( clientX - _this.screen.offsetLeft ) / _this.radius * 0.5,
+			( clientY - _this.screen.offsetTop ) / _this.radius * 0.5
+		);
+
+	};
+
+	this.getMouseProjectionOnBall = function ( clientX, clientY ) {
+
+		var mouseOnBall = new THREE.Vector3(
+			( clientX - _this.screen.width * 0.5 - _this.screen.offsetLeft ) / _this.radius,
+			( _this.screen.height * 0.5 + _this.screen.offsetTop - clientY ) / _this.radius,
+			0.0
+		);
+
+		var length = mouseOnBall.length();
+
+		if ( length > 1.0 ) {
+
+			mouseOnBall.normalize();
+
+		} else {
+
+			mouseOnBall.z = Math.sqrt( 1.0 - length * length );
+
+		}
+
+		_eye.copy( _this.object.position ).sub( _this.target );
+
+		var projection = _this.object.up.clone().setLength( mouseOnBall.y );
+		projection.add( _this.object.up.clone().cross( _eye ).setLength( mouseOnBall.x ) );
+		projection.add( _eye.setLength( mouseOnBall.z ) );
+
+		return projection;
+
+	};
+
+	this.rotateCamera = function () {
+
+		var angle = Math.acos( _rotateStart.dot( _rotateEnd ) / _rotateStart.length() / _rotateEnd.length() );
+
+		if ( angle ) {
+
+			var axis = ( new THREE.Vector3() ).crossVectors( _rotateStart, _rotateEnd ).normalize(),
+				quaternion = new THREE.Quaternion();
+
+			angle *= _this.rotateSpeed;
+
+			quaternion.setFromAxisAngle( axis, -angle );
+
+			_eye.applyQuaternion( quaternion );
+			_this.object.up.applyQuaternion( quaternion );
+
+			_rotateEnd.applyQuaternion( quaternion );
+
+			if ( _this.staticMoving ) {
+
+				_rotateStart.copy( _rotateEnd );
+
+			} else {
+
+				quaternion.setFromAxisAngle( axis, angle * ( _this.dynamicDampingFactor - 1.0 ) );
+				_rotateStart.applyQuaternion( quaternion );
+
+			}
+
+		}
+
+	};
+
+	this.zoomCamera = function () {
+
+		if ( _state === STATE.TOUCH_ZOOM ) {
+
+			var factor = _touchZoomDistanceStart / _touchZoomDistanceEnd;
+			_touchZoomDistanceStart = _touchZoomDistanceEnd;
+			_zoomFactor *= factor;
+
+			_this.object.left = _zoomFactor * _this.left0 + ( 1 - _zoomFactor ) *  _this.center0.x;
+			_this.object.right = _zoomFactor * _this.right0 + ( 1 - _zoomFactor ) *  _this.center0.x;
+			_this.object.top = _zoomFactor * _this.top0 + ( 1 - _zoomFactor ) *  _this.center0.y;
+			_this.object.bottom = _zoomFactor * _this.bottom0 + ( 1 - _zoomFactor ) *  _this.center0.y;
+
+		} else {
+
+			var factor = 1.0 + ( _zoomEnd.y - _zoomStart.y ) * _this.zoomSpeed;
+
+			if ( factor !== 1.0 && factor > 0.0 ) {
+				_zoomFactor *= factor;
+
+				_this.object.left = _zoomFactor * _this.left0 + ( 1 - _zoomFactor ) *  _this.center0.x;
+				_this.object.right = _zoomFactor * _this.right0 + ( 1 - _zoomFactor ) *  _this.center0.x;
+				_this.object.top = _zoomFactor * _this.top0 + ( 1 - _zoomFactor ) *  _this.center0.y;
+				_this.object.bottom = _zoomFactor * _this.bottom0 + ( 1 - _zoomFactor ) *  _this.center0.y;
+
+				if ( _this.staticMoving ) {
+
+					_zoomStart.copy( _zoomEnd );
+
+				} else {
+
+					_zoomStart.y += ( _zoomEnd.y - _zoomStart.y ) * this.dynamicDampingFactor;
+
+				}
+
+			}
+
+		}
+
+	};
+
+	this.panCamera = function () {
+
+		var mouseChange = _panEnd.clone().sub( _panStart );
+
+		if ( mouseChange.lengthSq() ) {
+
+			mouseChange.multiplyScalar( _eye.length() * _this.panSpeed );
+
+			var pan = _eye.clone().cross( _this.object.up ).setLength( mouseChange.x );
+			pan.add( _this.object.up.clone().setLength( mouseChange.y ) );
+
+			_this.object.position.add( pan );
+			_this.target.add( pan );
+
+			if ( _this.staticMoving ) {
+
+				_panStart = _panEnd;
+
+			} else {
+
+				_panStart.add( mouseChange.subVectors( _panEnd, _panStart ).multiplyScalar( _this.dynamicDampingFactor ) );
+
+			}
+
+		}
+
+	};
+
+	this.update = function () {
+
+		_eye.subVectors( _this.object.position, _this.target );
+
+		if ( !_this.noRotate ) {
+
+			_this.rotateCamera();
+
+		}
+
+		if ( !_this.noZoom ) {
+
+			_this.zoomCamera();
+			_this.object.updateProjectionMatrix();
+
+		}
+
+		if ( !_this.noPan ) {
+
+			_this.panCamera();
+
+		}
+
+		_this.object.position.addVectors( _this.target, _eye );
+
+		_this.object.lookAt( _this.target );
+
+		if ( lastPosition.distanceToSquared( _this.object.position ) > 0 ) {
+
+			_this.dispatchEvent( changeEvent );
+
+			lastPosition.copy( _this.object.position );
+
+		}
+
+	};
+
+	this.reset = function () {
+
+		_state = STATE.NONE;
+		_prevState = STATE.NONE;
+
+		_this.target.copy( _this.target0 );
+		_this.object.position.copy( _this.position0 );
+		_this.object.up.copy( _this.up0 );
+
+		_eye.subVectors( _this.object.position, _this.target );
+
+		_this.object.left = _this.left0;
+		_this.object.right = _this.right0;
+		_this.object.top = _this.top0;
+		_this.object.bottom = _this.bottom0;
+
+		_this.object.lookAt( _this.target );
+
+		_this.dispatchEvent( changeEvent );
+
+		lastPosition.copy( _this.object.position );
+
+	};
+
+	// listeners
+
+	function keydown( event ) {
+
+		if ( _this.enabled === false ) return;
+
+		window.removeEventListener( 'keydown', keydown );
+
+		_prevState = _state;
+
+		if ( _state !== STATE.NONE ) {
+
+			return;
+
+		} else if ( event.keyCode === _this.keys[ STATE.ROTATE ] && !_this.noRotate ) {
+
+			_state = STATE.ROTATE;
+
+		} else if ( event.keyCode === _this.keys[ STATE.ZOOM ] && !_this.noZoom ) {
+
+			_state = STATE.ZOOM;
+
+		} else if ( event.keyCode === _this.keys[ STATE.PAN ] && !_this.noPan ) {
+
+			_state = STATE.PAN;
+
+		}
+
+	}
+
+	function keyup( event ) {
+
+		if ( _this.enabled === false ) return;
+
+		_state = _prevState;
+
+		window.addEventListener( 'keydown', keydown, false );
+
+	}
+
+	function mousedown( event ) {
+
+		if ( _this.enabled === false ) return;
+
+		event.preventDefault();
+		event.stopPropagation();
+
+		if ( _state === STATE.NONE ) {
+
+			_state = event.button;
+
+		}
+
+		if ( _state === STATE.ROTATE && !_this.noRotate ) {
+
+			_rotateStart = _rotateEnd = _this.getMouseProjectionOnBall( event.clientX, event.clientY );
+
+		} else if ( _state === STATE.ZOOM && !_this.noZoom ) {
+
+			_zoomStart = _zoomEnd = _this.getMouseOnScreen( event.clientX, event.clientY );
+
+		} else if ( _state === STATE.PAN && !_this.noPan ) {
+
+			_panStart = _panEnd = _this.getMouseOnScreen( event.clientX, event.clientY );
+
+		}
+
+		document.addEventListener( 'mousemove', mousemove, false );
+		document.addEventListener( 'mouseup', mouseup, false );
+
+	}
+
+	function mousemove( event ) {
+
+		if ( _this.enabled === false ) return;
+
+		event.preventDefault();
+		event.stopPropagation();
+
+		if ( _state === STATE.ROTATE && !_this.noRotate ) {
+
+			_rotateEnd = _this.getMouseProjectionOnBall( event.clientX, event.clientY );
+
+		} else if ( _state === STATE.ZOOM && !_this.noZoom ) {
+
+			_zoomEnd = _this.getMouseOnScreen( event.clientX, event.clientY );
+
+		} else if ( _state === STATE.PAN && !_this.noPan ) {
+
+			_panEnd = _this.getMouseOnScreen( event.clientX, event.clientY );
+
+		}
+
+	}
+
+	function mouseup( event ) {
+
+		if ( _this.enabled === false ) return;
+
+		event.preventDefault();
+		event.stopPropagation();
+
+		_state = STATE.NONE;
+
+		document.removeEventListener( 'mousemove', mousemove );
+		document.removeEventListener( 'mouseup', mouseup );
+
+	}
+
+	function mousewheel( event ) {
+
+		if ( _this.enabled === false ) return;
+
+		event.preventDefault();
+		event.stopPropagation();
+
+		var delta = 0;
+
+		if ( event.wheelDelta ) { // WebKit / Opera / Explorer 9
+
+			delta = event.wheelDelta / 40;
+
+		} else if ( event.detail ) { // Firefox
+
+			delta = - event.detail / 3;
+
+		}
+
+		_zoomStart.y += delta * 0.01;
+
+	}
+
+	function touchstart( event ) {
+
+		if ( _this.enabled === false ) return;
+
+		switch ( event.touches.length ) {
+
+			case 1:
+				_state = STATE.TOUCH_ROTATE;
+				_rotateStart = _rotateEnd = _this.getMouseProjectionOnBall( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
+				break;
+
+			case 2:
+				_state = STATE.TOUCH_ZOOM;
+				var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
+				var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
+				_touchZoomDistanceEnd = _touchZoomDistanceStart = Math.sqrt( dx * dx + dy * dy );
+				break;
+
+			case 3:
+				_state = STATE.TOUCH_PAN;
+				_panStart = _panEnd = _this.getMouseOnScreen( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
+				break;
+
+			default:
+				_state = STATE.NONE;
+
+		}
+
+	}
+
+	function touchmove( event ) {
+
+		if ( _this.enabled === false ) return;
+
+		event.preventDefault();
+		event.stopPropagation();
+
+		switch ( event.touches.length ) {
+
+			case 1:
+				_rotateEnd = _this.getMouseProjectionOnBall( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
+				break;
+
+			case 2:
+				var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
+				var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
+				_touchZoomDistanceEnd = Math.sqrt( dx * dx + dy * dy )
+				break;
+
+			case 3:
+				_panEnd = _this.getMouseOnScreen( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
+				break;
+
+			default:
+				_state = STATE.NONE;
+
+		}
+
+	}
+
+	function touchend( event ) {
+
+		if ( _this.enabled === false ) return;
+
+		switch ( event.touches.length ) {
+
+			case 1:
+				_rotateStart = _rotateEnd = _this.getMouseProjectionOnBall( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
+				break;
+
+			case 2:
+				_touchZoomDistanceStart = _touchZoomDistanceEnd = 0;
+				break;
+
+			case 3:
+				_panStart = _panEnd = _this.getMouseOnScreen( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
+				break;
+
+		}
+
+		_state = STATE.NONE;
+
+	}
+
+	this.domElement.addEventListener( 'contextmenu', function ( event ) { event.preventDefault(); }, false );
+
+	this.domElement.addEventListener( 'mousedown', mousedown, false );
+
+	this.domElement.addEventListener( 'mousewheel', mousewheel, false );
+	this.domElement.addEventListener( 'DOMMouseScroll', mousewheel, false ); // firefox
+
+	this.domElement.addEventListener( 'touchstart', touchstart, false );
+	this.domElement.addEventListener( 'touchend', touchend, false );
+	this.domElement.addEventListener( 'touchmove', touchmove, false );
+
+	window.addEventListener( 'keydown', keydown, false );
+	window.addEventListener( 'keyup', keyup, false );
+
+	this.handleResize();
+
+};
+
+THREE.OrthographicTrackballControls.prototype = Object.create( THREE.EventDispatcher.prototype );

+ 0 - 327
examples/js/controls/PathControls.js

@@ -1,327 +0,0 @@
-/**
- * @author alteredq / http://alteredqualia.com/
- */
-
-THREE.PathControls = function ( object, domElement ) {
-
-	this.object = object;
-	this.domElement = ( domElement !== undefined ) ? domElement : document;
-
-	this.id = "PathControls" + THREE.PathControlsIdCounter ++;
-
-	// API
-
-	this.duration = 10 * 1000; // milliseconds
-	this.waypoints = [];
-
-	this.useConstantSpeed = true;
-	this.resamplingCoef = 50;
-
-	this.debugPath = new THREE.Object3D();
-	this.debugDummy = new THREE.Object3D();
-
-	this.animationParent = new THREE.Object3D();
-
-	this.lookSpeed = 0.005;
-	this.lookVertical = true;
-	this.lookHorizontal = true;
-	this.verticalAngleMap   = { srcRange: [ 0, 2 * Math.PI ], dstRange: [ 0, 2 * Math.PI ] };
-	this.horizontalAngleMap = { srcRange: [ 0, 2 * Math.PI ], dstRange: [ 0, 2 * Math.PI ] };
-
-	// internals
-
-	this.target = new THREE.Object3D();
-
-	this.mouseX = 0;
-	this.mouseY = 0;
-
-	this.lat = 0;
-	this.lon = 0;
-
-	this.phi = 0;
-	this.theta = 0;
-
-	var PI2 = Math.PI * 2;
-
-	this.viewHalfX = 0;
-	this.viewHalfY = 0;
-
-	if ( this.domElement !== document ) {
-
-		this.domElement.setAttribute( 'tabindex', -1 );
-
-	}
-
-	// methods
-
-	this.handleResize = function () {
-
-		if ( this.domElement === document ) {
-
-			this.viewHalfX = window.innerWidth / 2;
-			this.viewHalfY = window.innerHeight / 2;
-
-		} else {
-
-			this.viewHalfX = this.domElement.offsetWidth / 2;
-			this.viewHalfY = this.domElement.offsetHeight / 2;
-
-		}
-
-	};
-
-	this.update = function ( delta ) {
-
-		var srcRange, dstRange;
-
-		if( this.lookHorizontal ) this.lon += this.mouseX * this.lookSpeed * delta;
-		if( this.lookVertical )   this.lat -= this.mouseY * this.lookSpeed * delta;
-
-		this.lon = Math.max( 0, Math.min( 360, this.lon ) );
-		this.lat = Math.max( - 85, Math.min( 85, this.lat ) );
-
-		this.phi = THREE.Math.degToRad( 90 - this.lat );
-		this.theta = THREE.Math.degToRad( this.lon );
-
-		this.phi = normalize_angle_rad( this.phi );
-
-		// constrain vertical look angle
-
-		srcRange = this.verticalAngleMap.srcRange;
-		dstRange = this.verticalAngleMap.dstRange;
-
-		var tmpPhi = THREE.Math.mapLinear( this.phi, srcRange[ 0 ], srcRange[ 1 ], dstRange[ 0 ], dstRange[ 1 ] );
-		var tmpPhiFullRange = dstRange[ 1 ] - dstRange[ 0 ];
-		var tmpPhiNormalized = ( tmpPhi - dstRange[ 0 ] ) / tmpPhiFullRange;
-
-		this.phi = QuadraticEaseInOut( tmpPhiNormalized ) * tmpPhiFullRange + dstRange[ 0 ];
-
-		// constrain horizontal look angle
-
-		srcRange = this.horizontalAngleMap.srcRange;
-		dstRange = this.horizontalAngleMap.dstRange;
-
-		var tmpTheta = THREE.Math.mapLinear( this.theta, srcRange[ 0 ], srcRange[ 1 ], dstRange[ 0 ], dstRange[ 1 ] );
-		var tmpThetaFullRange = dstRange[ 1 ] - dstRange[ 0 ];
-		var tmpThetaNormalized = ( tmpTheta - dstRange[ 0 ] ) / tmpThetaFullRange;
-
-		this.theta = QuadraticEaseInOut( tmpThetaNormalized ) * tmpThetaFullRange + dstRange[ 0 ];
-
-		var targetPosition = this.target.position,
-			position = this.object.position;
-
-		targetPosition.x = 100 * Math.sin( this.phi ) * Math.cos( this.theta );
-		targetPosition.y = 100 * Math.cos( this.phi );
-		targetPosition.z = 100 * Math.sin( this.phi ) * Math.sin( this.theta );
-
-		this.object.lookAt( this.target.position );
-
-	};
-
-	this.onMouseMove = function ( event ) {
-
-		if ( this.domElement === document ) {
-
-			this.mouseX = event.pageX - this.viewHalfX;
-			this.mouseY = event.pageY - this.viewHalfY;
-
-		} else {
-
-			this.mouseX = event.pageX - this.domElement.offsetLeft - this.viewHalfX;
-			this.mouseY = event.pageY - this.domElement.offsetTop - this.viewHalfY;
-
-		}
-
-	};
-
-	// utils
-
-	function normalize_angle_rad( a ) {
-
-		var b = a % PI2;
-		return b >= 0 ? b : b + PI2;
-
-	};
-
-	function distance( a, b ) {
-
-		var dx = a[ 0 ] - b[ 0 ],
-			dy = a[ 1 ] - b[ 1 ],
-			dz = a[ 2 ] - b[ 2 ];
-
-		return Math.sqrt( dx * dx + dy * dy + dz * dz );
-
-	};
-
-	function QuadraticEaseInOut ( k ) {
-
-		if ( ( k *= 2 ) < 1 ) return 0.5 * k * k;
-		return - 0.5 * ( --k * ( k - 2 ) - 1 );
-
-	};
-
-	function bind( scope, fn ) {
-
-		return function () {
-
-			fn.apply( scope, arguments );
-
-		};
-
-	};
-
-	function initAnimationPath( parent, spline, name, duration ) {
-
-		var animationData = {
-
-		   name: name,
-		   fps: 0.6,
-		   length: duration,
-
-		   hierarchy: []
-
-		};
-
-		var i,
-			parentAnimation, childAnimation,
-			path = spline.getControlPointsArray(),
-			sl = spline.getLength(),
-			pl = path.length,
-			t = 0,
-			first = 0,
-			last  = pl - 1;
-
-		parentAnimation = { parent: -1, keys: [] };
-		parentAnimation.keys[ first ] = { time: 0,        pos: path[ first ], rot: [ 0, 0, 0, 1 ], scl: [ 1, 1, 1 ] };
-		parentAnimation.keys[ last  ] = { time: duration, pos: path[ last ],  rot: [ 0, 0, 0, 1 ], scl: [ 1, 1, 1 ] };
-
-		for ( i = 1; i < pl - 1; i++ ) {
-
-			// real distance (approximation via linear segments)
-
-			t = duration * sl.chunks[ i ] / sl.total;
-
-			// equal distance
-
-			//t = duration * ( i / pl );
-
-			// linear distance
-
-			//t += duration * distance( path[ i ], path[ i - 1 ] ) / sl.total;
-
-			parentAnimation.keys[ i ] = { time: t, pos: path[ i ] };
-
-		}
-
-		animationData.hierarchy[ 0 ] = parentAnimation;
-
-		THREE.AnimationHandler.add( animationData );
-
-		var animation = new THREE.Animation( parent, name );
-		animation.interpolationType = THREE.AnimationHandler.CATMULLROM_FORWARD;
-
-		return animation;
-
-	};
-
-
-	function createSplineGeometry( spline, n_sub ) {
-
-		var i, index, position,
-			geometry = new THREE.Geometry();
-
-		for ( i = 0; i < spline.points.length * n_sub; i ++ ) {
-
-			index = i / ( spline.points.length * n_sub );
-			position = spline.getPoint( index );
-
-			geometry.vertices[ i ] = new THREE.Vector3( position.x, position.y, position.z );
-
-		}
-
-		return geometry;
-
-	};
-
-	function createPath( parent, spline ) {
-
-		var lineGeo = createSplineGeometry( spline, 10 ),
-			particleGeo = createSplineGeometry( spline, 10 ),
-			lineMat = new THREE.LineBasicMaterial( { color: 0xff0000, linewidth: 3 } ),
-			lineObj = new THREE.Line( lineGeo, lineMat ),
-			particleObj = new THREE.ParticleSystem( particleGeo, new THREE.ParticleSystemMaterial( { color: 0xffaa00, size: 3 } ) );
-
-		lineObj.scale.set( 1, 1, 1 );
-		parent.add( lineObj );
-
-		particleObj.scale.set( 1, 1, 1 );
-		parent.add( particleObj );
-
-		var waypoint,
-			geo = new THREE.SphereGeometry( 1, 16, 8 ),
-			mat = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );
-
-		for ( var i = 0; i < spline.points.length; i ++ ) {
-
-			waypoint = new THREE.Mesh( geo, mat );
-			waypoint.position.copy( spline.points[ i ] );
-			parent.add( waypoint );
-
-		}
-
-	};
-
-	this.init = function ( ) {
-
-		// constructor
-
-		this.spline = new THREE.Spline();
-		this.spline.initFromArray( this.waypoints );
-
-		if ( this.useConstantSpeed ) {
-
-			this.spline.reparametrizeByArcLength( this.resamplingCoef );
-
-		}
-
-		if ( this.createDebugDummy ) {
-
-			var dummyParentMaterial = new THREE.MeshLambertMaterial( { color: 0x0077ff } ),
-			dummyChildMaterial  = new THREE.MeshLambertMaterial( { color: 0x00ff00 } ),
-			dummyParentGeo = new THREE.BoxGeometry( 10, 10, 20 ),
-			dummyChildGeo  = new THREE.BoxGeometry( 2, 2, 10 );
-
-			this.animationParent = new THREE.Mesh( dummyParentGeo, dummyParentMaterial );
-
-			var dummyChild = new THREE.Mesh( dummyChildGeo, dummyChildMaterial );
-			dummyChild.position.set( 0, 10, 0 );
-
-			this.animation = initAnimationPath( this.animationParent, this.spline, this.id, this.duration );
-
-			this.animationParent.add( this.object );
-			this.animationParent.add( this.target );
-			this.animationParent.add( dummyChild );
-
-		} else {
-
-			this.animation = initAnimationPath( this.animationParent, this.spline, this.id, this.duration );
-			this.animationParent.add( this.target );
-			this.animationParent.add( this.object );
-
-		}
-
-		if ( this.createDebugPath ) {
-
-			createPath( this.debugPath, this.spline );
-
-		}
-
-		this.domElement.addEventListener( 'mousemove', bind( this, this.onMouseMove ), false );
-
-	};
-
-	this.handleResize();
-
-};
-
-THREE.PathControlsIdCounter = 0;

+ 53 - 41
examples/js/controls/TrackballControls.js

@@ -6,7 +6,7 @@
 THREE.TrackballControls = function ( object, domElement ) {
 
 	var _this = this;
-	var STATE = { NONE: -1, ROTATE: 0, ZOOM: 1, PAN: 2, TOUCH_ROTATE: 3, TOUCH_ZOOM: 4, TOUCH_PAN: 5 };
+	var STATE = { NONE: -1, ROTATE: 0, ZOOM: 1, PAN: 2, TOUCH_ROTATE: 3, TOUCH_ZOOM_PAN: 4 };
 
 	this.object = object;
 	this.domElement = ( domElement !== undefined ) ? domElement : document;
@@ -107,22 +107,30 @@ THREE.TrackballControls = function ( object, domElement ) {
 
 	};
 
-	this.getMouseOnScreen = function ( pageX, pageY, vector ) {
+	var getMouseOnScreen = ( function () {
 
-		return vector.set(
-			( pageX - _this.screen.left ) / _this.screen.width,
-			( pageY - _this.screen.top ) / _this.screen.height
-		);
+		var vector = new THREE.Vector2();
 
-	};
+		return function ( pageX, pageY ) {
+
+			vector.set(
+				( pageX - _this.screen.left ) / _this.screen.width,
+				( pageY - _this.screen.top ) / _this.screen.height
+			);
+
+			return vector;
 
-	this.getMouseProjectionOnBall = (function(){
+		};
 
-		var objectUp = new THREE.Vector3(),
-		    mouseOnBall = new THREE.Vector3();
+	}() );
 
+	var getMouseProjectionOnBall = ( function () {
 
-		return function ( pageX, pageY, projection ) {
+		var vector = new THREE.Vector3();
+		var objectUp = new THREE.Vector3();
+		var mouseOnBall = new THREE.Vector3();
+
+		return function ( pageX, pageY ) {
 
 			mouseOnBall.set(
 				( pageX - _this.screen.width * 0.5 - _this.screen.left ) / (_this.screen.width*.5),
@@ -156,14 +164,15 @@ THREE.TrackballControls = function ( object, domElement ) {
 
 			_eye.copy( _this.object.position ).sub( _this.target );
 
-			projection.copy( _this.object.up ).setLength( mouseOnBall.y )
-			projection.add( objectUp.copy( _this.object.up ).cross( _eye ).setLength( mouseOnBall.x ) );
-			projection.add( _eye.setLength( mouseOnBall.z ) );
+			vector.copy( _this.object.up ).setLength( mouseOnBall.y )
+			vector.add( objectUp.copy( _this.object.up ).cross( _eye ).setLength( mouseOnBall.x ) );
+			vector.add( _eye.setLength( mouseOnBall.z ) );
 
-			return projection;
-		}
+			return vector;
 
-	}());
+		};
+
+	}() );
 
 	this.rotateCamera = (function(){
 
@@ -206,7 +215,7 @@ THREE.TrackballControls = function ( object, domElement ) {
 
 	this.zoomCamera = function () {
 
-		if ( _state === STATE.TOUCH_ZOOM ) {
+		if ( _state === STATE.TOUCH_ZOOM_PAN ) {
 
 			var factor = _touchZoomDistanceStart / _touchZoomDistanceEnd;
 			_touchZoomDistanceStart = _touchZoomDistanceEnd;
@@ -403,25 +412,25 @@ THREE.TrackballControls = function ( object, domElement ) {
 
 		if ( _state === STATE.ROTATE && !_this.noRotate ) {
 
-			_this.getMouseProjectionOnBall( event.pageX, event.pageY, _rotateStart );
-			_rotateEnd.copy(_rotateStart)
+			_rotateStart.copy( getMouseProjectionOnBall( event.pageX, event.pageY ) );
+			_rotateEnd.copy( _rotateStart );
 
 		} else if ( _state === STATE.ZOOM && !_this.noZoom ) {
 
-			_this.getMouseOnScreen( event.pageX, event.pageY, _zoomStart );
+			_zoomStart.copy( getMouseOnScreen( event.pageX, event.pageY ) );
 			_zoomEnd.copy(_zoomStart);
 
 		} else if ( _state === STATE.PAN && !_this.noPan ) {
 
-			_this.getMouseOnScreen( event.pageX, event.pageY, _panStart );
+			_panStart.copy( getMouseOnScreen( event.pageX, event.pageY ) );
 			_panEnd.copy(_panStart)
 
 		}
 
 		document.addEventListener( 'mousemove', mousemove, false );
 		document.addEventListener( 'mouseup', mouseup, false );
-		_this.dispatchEvent( startEvent );
 
+		_this.dispatchEvent( startEvent );
 
 	}
 
@@ -434,15 +443,15 @@ THREE.TrackballControls = function ( object, domElement ) {
 
 		if ( _state === STATE.ROTATE && !_this.noRotate ) {
 
-			_this.getMouseProjectionOnBall( event.pageX, event.pageY, _rotateEnd );
+			_rotateEnd.copy( getMouseProjectionOnBall( event.pageX, event.pageY ) );
 
 		} else if ( _state === STATE.ZOOM && !_this.noZoom ) {
 
-			_this.getMouseOnScreen( event.pageX, event.pageY, _zoomEnd );
+			_zoomEnd.copy( getMouseOnScreen( event.pageX, event.pageY ) );
 
 		} else if ( _state === STATE.PAN && !_this.noPan ) {
 
-			_this.getMouseOnScreen( event.pageX, event.pageY, _panEnd );
+			_panEnd.copy( getMouseOnScreen( event.pageX, event.pageY ) );
 
 		}
 
@@ -496,19 +505,20 @@ THREE.TrackballControls = function ( object, domElement ) {
 
 			case 1:
 				_state = STATE.TOUCH_ROTATE;
-				_rotateEnd.copy( _this.getMouseProjectionOnBall( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY, _rotateStart ));
+				_rotateStart.copy( getMouseProjectionOnBall( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ) );
+				_rotateEnd.copy( _rotateStart );
 				break;
 
 			case 2:
-				_state = STATE.TOUCH_ZOOM;
+				_state = STATE.TOUCH_ZOOM_PAN;
 				var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
 				var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
 				_touchZoomDistanceEnd = _touchZoomDistanceStart = Math.sqrt( dx * dx + dy * dy );
-				break;
 
-			case 3:
-				_state = STATE.TOUCH_PAN;
-				_panEnd.copy( _this.getMouseOnScreen( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY, _panStart ));
+				var x = ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX ) / 2;
+				var y = ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY ) / 2;
+				_panStart.copy( getMouseOnScreen( x, y ) );
+				_panEnd.copy( _panStart );
 				break;
 
 			default:
@@ -530,17 +540,17 @@ THREE.TrackballControls = function ( object, domElement ) {
 		switch ( event.touches.length ) {
 
 			case 1:
-				_this.getMouseProjectionOnBall( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY, _rotateEnd );
+				_rotateEnd.copy( getMouseProjectionOnBall( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ) );
 				break;
 
 			case 2:
 				var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
 				var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
-				_touchZoomDistanceEnd = Math.sqrt( dx * dx + dy * dy )
-				break;
+				_touchZoomDistanceEnd = Math.sqrt( dx * dx + dy * dy );
 
-			case 3:
-				_this.getMouseOnScreen( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY, _panEnd );
+				var x = ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX ) / 2;
+				var y = ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY ) / 2;
+				_panEnd.copy( getMouseOnScreen( x, y ) );
 				break;
 
 			default:
@@ -557,15 +567,17 @@ THREE.TrackballControls = function ( object, domElement ) {
 		switch ( event.touches.length ) {
 
 			case 1:
-				_rotateStart.copy( _this.getMouseProjectionOnBall( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY, _rotateEnd ));
+				_rotateEnd.copy( getMouseProjectionOnBall( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ) );
+				_rotateStart.copy( _rotateEnd );
 				break;
 
 			case 2:
 				_touchZoomDistanceStart = _touchZoomDistanceEnd = 0;
-				break;
 
-			case 3:
-				_panStart.copy( _this.getMouseOnScreen( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY, _panEnd ));
+				var x = ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX ) / 2;
+				var y = ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY ) / 2;
+				_panEnd.copy( getMouseOnScreen( x, y ) );
+				_panStart.copy( _panEnd );
 				break;
 
 		}

+ 3 - 3
examples/js/controls/TransformControls.js

@@ -720,7 +720,7 @@
 
 			event.preventDefault();
 
-			var pointer = event.touches ? event.touches[ 0 ] : event;
+			var pointer = event.changedTouches ? event.changedTouches[ 0 ] : event;
 
 			var intersect = intersectObjects( pointer, scope.gizmo[_mode].pickers.children );
 
@@ -747,7 +747,7 @@
 			event.preventDefault();
 			event.stopPropagation();
 
-			var pointer = event.touches ? event.touches[ 0 ] : event;
+			var pointer = event.changedTouches ? event.changedTouches[ 0 ] : event;
 
 			if ( pointer.button === 0 || pointer.button === undefined ) {
 
@@ -791,7 +791,7 @@
 			event.preventDefault();
 			event.stopPropagation();
 
-			var pointer = event.touches? event.touches[0] : event;
+			var pointer = event.changedTouches? event.changedTouches[0] : event;
 
 			var planeIntersect = intersectObjects( pointer, [scope.gizmo[_mode].activePlane] );
 

+ 77 - 0
examples/js/controls/VRControls.js

@@ -0,0 +1,77 @@
+/**
+ * @author dmarcos / https://github.com/dmarcos
+ */
+
+THREE.VRControls = function ( camera, done ) {
+
+	this._camera = camera;
+
+	this._init = function () {
+		var self = this;
+		if ( !navigator.mozGetVRDevices && !navigator.getVRDevices ) {
+			if ( done ) {
+				done("Your browser is not VR Ready");
+			}
+			return;
+		}
+		if ( navigator.getVRDevices ) {
+			navigator.getVRDevices().then( gotVRDevices );
+		} else {
+			navigator.mozGetVRDevices( gotVRDevices );
+		}
+		function gotVRDevices( devices ) {
+			var vrInput;
+			var error;
+			for ( var i = 0; i < devices.length; ++i ) {
+				if ( devices[i] instanceof PositionSensorVRDevice ) {
+					vrInput = devices[i]
+					self._vrInput = vrInput;
+					break; // We keep the first we encounter
+				}
+			}
+			if ( done ) {
+				if ( !vrInput ) {
+				 error = 'HMD not available';
+				}
+				done( error );
+			}
+		}
+	};
+
+	this._init();
+
+	this.update = function() {
+		var camera = this._camera;
+		var quat;
+		var vrState = this.getVRState();
+		if ( !vrState ) {
+			return;
+		}
+		// Applies head rotation from sensors data.
+		if ( camera ) {
+			camera.quaternion.fromArray( vrState.hmd.rotation );
+		}
+	};
+
+	this.getVRState = function() {
+		var vrInput = this._vrInput;
+		var orientation;
+		var vrState;
+		if ( !vrInput ) {
+			return null;
+		}
+		orientation	= vrInput.getState().orientation;
+		vrState = {
+			hmd : {
+				rotation : [
+					orientation.x,
+					orientation.y,
+					orientation.z,
+					orientation.w
+				]
+			}
+		};
+		return vrState;
+	};
+
+};

+ 22 - 15
examples/js/effects/CrosseyedEffect.js → examples/js/effects/StereoEffect.js

@@ -1,22 +1,25 @@
 /**
  * @author alteredq / http://alteredqualia.com/
+ * @authod mrdoob / http://mrdoob.com/
+ * @authod arodic / http://aleksandarrodic.com/
  */
 
-THREE.CrosseyedEffect = function ( renderer ) {
+THREE.StereoEffect = function ( renderer ) {
 
 	// API
 
-	this.separation = 10;
+	this.separation = 3;
 
 	// internals
 
 	var _width, _height;
 
-	var _cameraL = new THREE.PerspectiveCamera();
-	_cameraL.target = new THREE.Vector3();
+	var _position = new THREE.Vector3();
+	var _quaternion = new THREE.Quaternion();
+	var _scale = new THREE.Vector3();
 
+	var _cameraL = new THREE.PerspectiveCamera();
 	var _cameraR = new THREE.PerspectiveCamera();
-	_cameraR.target = new THREE.Vector3();
 
 	// initialization
 
@@ -33,6 +36,12 @@ THREE.CrosseyedEffect = function ( renderer ) {
 
 	this.render = function ( scene, camera ) {
 
+		scene.updateMatrixWorld();
+
+		if ( camera.parent === undefined ) camera.updateMatrixWorld();
+	
+		camera.matrixWorld.decompose( _position, _quaternion, _scale );
+
 		// left
 
 		_cameraL.fov = camera.fov;
@@ -41,32 +50,30 @@ THREE.CrosseyedEffect = function ( renderer ) {
 		_cameraL.far = camera.far;
 		_cameraL.updateProjectionMatrix();
 
-		_cameraL.position.copy( camera.position );
-		_cameraL.target.copy( camera.target );
-		_cameraL.translateX( this.separation );
-		_cameraL.lookAt( _cameraL.target );
+		_cameraL.position.copy( _position );
+		_cameraL.quaternion.copy( _quaternion );
+		_cameraL.translateX( - this.separation );
 
 		// right
 
 		_cameraR.near = camera.near;
 		_cameraR.far = camera.far;
-
 		_cameraR.projectionMatrix = _cameraL.projectionMatrix;
 
-		_cameraR.position.copy( camera.position );
-		_cameraR.target.copy( camera.target );
-		_cameraR.translateX( - this.separation );
-		_cameraR.lookAt( _cameraR.target );
+		_cameraR.position.copy( _position );
+		_cameraR.quaternion.copy( _quaternion );
+		_cameraR.translateX( this.separation );
 
 		//
 
+		renderer.setViewport( 0, 0, _width * 2, _height );
 		renderer.clear();
 
 		renderer.setViewport( 0, 0, _width, _height );
 		renderer.render( scene, _cameraL );
 
 		renderer.setViewport( _width, 0, _width, _height );
-		renderer.render( scene, _cameraR, false );
+		renderer.render( scene, _cameraR );
 
 	};
 

+ 233 - 0
examples/js/effects/VREffect.js

@@ -0,0 +1,233 @@
+/**
+ * @author dmarcos / https://github.com/dmarcos
+ *
+ * It handles stereo rendering
+ * If mozGetVRDevices and getVRDevices APIs are not available it gracefuly falls back to a
+ * regular renderer
+ *
+ * The HMD supported is the Oculus DK1 and The Web API doesn't currently allow
+ * to query for the display resolution (only the chrome API allows it).
+ * The dimensions of the screen are temporarly hardcoded (1280 x 800).
+ *
+ * For VR mode to work it has to be used with the Oculus enabled builds of Firefox or Chrome:
+ *
+ * Firefox:
+ *
+ * OSX: http://people.mozilla.com/~vladimir/vr/firefox-33.0a1.en-US.mac.dmg
+ * WIN: http://people.mozilla.com/~vladimir/vr/firefox-33.0a1.en-US.win64-x86_64.zip
+ *
+ * Chrome builds:
+ *
+ * https://drive.google.com/folderview?id=0BzudLt22BqGRbW9WTHMtOWMzNjQ&usp=sharing#list
+ *
+ */
+THREE.VREffect = function ( renderer, done ) {
+	this._renderer = renderer;
+
+	this._init = function() {
+		var self = this;
+		if ( !navigator.mozGetVRDevices && !navigator.getVRDevices ) {
+			if ( done ) {
+				done("Your browser is not VR Ready");
+			}
+			return;
+		}
+		if ( navigator.getVRDevices ) {
+			navigator.getVRDevices().then( gotVRDevices );
+		} else {
+			navigator.mozGetVRDevices( gotVRDevices );
+		}
+		function gotVRDevices( devices ) {
+			var vrHMD;
+			var error;
+			for ( var i = 0; i < devices.length; ++i ) {
+				if ( devices[i] instanceof HMDVRDevice ) {
+					vrHMD = devices[i];
+					self._vrHMD = vrHMD;
+					self.leftEyeTranslation = vrHMD.getEyeTranslation( "left" );
+					self.rightEyeTranslation = vrHMD.getEyeTranslation( "right" );
+					self.leftEyeFOV = vrHMD.getRecommendedEyeFieldOfView( "left" );
+					self.rightEyeFOV = vrHMD.getRecommendedEyeFieldOfView( "right" );
+					break; // We keep the first we encounter
+				}
+			}
+			if ( done ) {
+				if ( !vrHMD ) {
+				 error = 'HMD not available';
+				}
+				done( error );
+			}
+		}
+	};
+
+	this._init();
+
+	this.render = function ( scene, camera ) {
+		var renderer = this._renderer;
+		var vrHMD = this._vrHMD;
+		renderer.enableScissorTest( false );
+		// VR render mode if HMD is available
+		if ( vrHMD ) {
+			this.renderStereo.apply( this, arguments );
+			return;
+		}
+		// Regular render mode if not HMD
+		renderer.render.apply( this._renderer , arguments );
+	};
+
+	this.renderStereo = function( scene, camera, renderTarget, forceClear ) {
+		var cameraLeft;
+		var cameraRight;
+		var leftEyeTranslation = this.leftEyeTranslation;
+		var rightEyeTranslation = this.rightEyeTranslation;
+		var renderer = this._renderer;
+		var rendererWidth = renderer.domElement.width / renderer.devicePixelRatio;
+		var rendererHeight = renderer.domElement.height / renderer.devicePixelRatio;
+		var eyeDivisionLine = rendererWidth / 2;
+		renderer.enableScissorTest( true );
+		renderer.clear();
+
+		// Grab camera matrix from user.
+		// This is interpreted as the head base.
+		if ( camera.matrixAutoUpdate ) {
+			camera.updateMatrix();
+		}
+		var eyeWorldMatrix = camera.matrixWorld.clone();
+
+		cameraLeft = camera.clone();
+		cameraRight = camera.clone();
+		cameraLeft.projectionMatrix = this.FovToProjection( this.leftEyeFOV );
+		cameraRight.projectionMatrix = this.FovToProjection( this.rightEyeFOV );
+		cameraLeft.position.add(new THREE.Vector3(
+			leftEyeTranslation.x, leftEyeTranslation.y, leftEyeTranslation.z) );
+		cameraRight.position.add(new THREE.Vector3(
+			rightEyeTranslation.x, rightEyeTranslation.y, rightEyeTranslation.z) );
+
+		// render left eye
+		renderer.setViewport( 0, 0, eyeDivisionLine, rendererHeight );
+		renderer.setScissor( 0, 0, eyeDivisionLine, rendererHeight );
+		renderer.render( scene, cameraLeft );
+
+		// render right eye
+		renderer.setViewport( eyeDivisionLine, 0, eyeDivisionLine, rendererHeight );
+		renderer.setScissor( eyeDivisionLine, 0, eyeDivisionLine, rendererHeight );
+		renderer.render( scene, cameraRight );
+
+	};
+
+	this.setFullScreen = function( enable ) {
+		var renderer = this._renderer;
+		var vrHMD = this._vrHMD;
+		var canvasOriginalSize = this._canvasOriginalSize;
+		if (!vrHMD) {
+			return;
+		}
+		// If state doesn't change we do nothing
+		if ( enable === this._fullScreen ) {
+			return;
+		}
+		this._fullScreen = !!enable;
+
+		// VR Mode disabled
+		if ( !enable ) {
+			// Restores canvas original size
+			renderer.setSize( canvasOriginalSize.width, canvasOriginalSize.height );
+			return;
+		}
+		// VR Mode enabled
+		this._canvasOriginalSize = {
+			width: renderer.domElement.width,
+			height: renderer.domElement.height
+		};
+		// Hardcoded Rift display size
+		renderer.setSize( 1280, 800, false );
+		this.startFullscreen();
+	};
+
+	this.startFullscreen = function() {
+		var self = this;
+		var renderer = this._renderer;
+		var vrHMD = this._vrHMD;
+		var canvas = renderer.domElement;
+		var fullScreenChange =
+			canvas.mozRequestFullScreen? 'mozfullscreenchange' : 'webkitfullscreenchange';
+
+		document.addEventListener( fullScreenChange, onFullScreenChanged, false );
+		function onFullScreenChanged() {
+			if ( !document.mozFullScreenElement && !document.webkitFullScreenElement ) {
+				self.setFullScreen( false );
+			}
+		}
+		if ( canvas.mozRequestFullScreen ) {
+			canvas.mozRequestFullScreen( { vrDisplay: vrHMD } );
+		} else {
+			canvas.webkitRequestFullscreen( { vrDisplay: vrHMD } );
+		}
+	};
+
+	this.FovToNDCScaleOffset = function( fov ) {
+		var pxscale = 2.0 / (fov.leftTan + fov.rightTan);
+		var pxoffset = (fov.leftTan - fov.rightTan) * pxscale * 0.5;
+		var pyscale = 2.0 / (fov.upTan + fov.downTan);
+		var pyoffset = (fov.upTan - fov.downTan) * pyscale * 0.5;
+		return { scale: [pxscale, pyscale], offset: [pxoffset, pyoffset] };
+	};
+
+	this.FovPortToProjection = function( fov, rightHanded /* = true */, zNear /* = 0.01 */, zFar /* = 10000.0 */ )
+	{
+		rightHanded = rightHanded === undefined ? true : rightHanded;
+		zNear = zNear === undefined ? 0.01 : zNear;
+		zFar = zFar === undefined ? 10000.0 : zFar;
+
+		var handednessScale = rightHanded ? -1.0 : 1.0;
+
+		// start with an identity matrix
+		var mobj = new THREE.Matrix4();
+		var m = mobj.elements;
+
+		// and with scale/offset info for normalized device coords
+		var scaleAndOffset = this.FovToNDCScaleOffset(fov);
+
+		// X result, map clip edges to [-w,+w]
+		m[0*4+0] = scaleAndOffset.scale[0];
+		m[0*4+1] = 0.0;
+		m[0*4+2] = scaleAndOffset.offset[0] * handednessScale;
+		m[0*4+3] = 0.0;
+
+		// Y result, map clip edges to [-w,+w]
+		// Y offset is negated because this proj matrix transforms from world coords with Y=up,
+		// but the NDC scaling has Y=down (thanks D3D?)
+		m[1*4+0] = 0.0;
+		m[1*4+1] = scaleAndOffset.scale[1];
+		m[1*4+2] = -scaleAndOffset.offset[1] * handednessScale;
+		m[1*4+3] = 0.0;
+
+		// Z result (up to the app)
+		m[2*4+0] = 0.0;
+		m[2*4+1] = 0.0;
+		m[2*4+2] = zFar / (zNear - zFar) * -handednessScale;
+		m[2*4+3] = (zFar * zNear) / (zNear - zFar);
+
+		// W result (= Z in)
+		m[3*4+0] = 0.0;
+		m[3*4+1] = 0.0;
+		m[3*4+2] = handednessScale;
+		m[3*4+3] = 0.0;
+
+		mobj.transpose();
+
+		return mobj;
+	};
+
+	this.FovToProjection = function( fov, rightHanded /* = true */, zNear /* = 0.01 */, zFar /* = 10000.0 */ )
+	{
+		var fovPort = {
+			upTan: Math.tan(fov.upDegrees * Math.PI / 180.0),
+			downTan: Math.tan(fov.downDegrees * Math.PI / 180.0),
+			leftTan: Math.tan(fov.leftDegrees * Math.PI / 180.0),
+			rightTan: Math.tan(fov.rightDegrees * Math.PI / 180.0)
+		};
+		return this.FovPortToProjection(fovPort, rightHanded, zNear, zFar);
+	};
+
+};

+ 7 - 0
examples/js/exporters/MaterialExporter.js

@@ -90,6 +90,13 @@ THREE.MaterialExporter.prototype = {
 
 			}
 
+		} else if ( material instanceof THREE.ShaderMaterial ) {
+
+			output.type = 'ShaderMaterial';
+			output.uniforms = material.uniforms;
+			output.vertexShader = material.vertexShader;
+			output.fragmentShader = material.fragmentShader;
+
 		} else if ( material instanceof THREE.SpriteMaterial ) {
 
 			output.type = 'SpriteMaterial';

+ 79 - 0
examples/js/exporters/STLBinaryExporter.js

@@ -0,0 +1,79 @@
+/**
+ * @author kovacsv / http://kovacsv.hu/
+ * @author mrdoob / http://mrdoob.com/
+ * @author mudcube / http://mudcu.be/
+ */
+ 
+THREE.STLBinaryExporter = function () {};
+
+THREE.STLBinaryExporter.prototype = {
+
+	constructor: THREE.STLBinaryExporter,
+
+	parse: ( function () {
+
+		var vector = new THREE.Vector3();
+		var normalMatrixWorld = new THREE.Matrix3();
+
+		return function ( scene ) {
+
+			var triangles = 0;
+			scene.traverse( function ( object ) {
+				if ( !(object instanceof THREE.Mesh) ) return;
+				triangles += object.geometry.faces.length;
+			});
+
+			var offset = 80; // skip header
+			var bufferLength = triangles * 2 + triangles * 3 * 4 * 4 + 80 + 4;
+			var arrayBuffer = new ArrayBuffer(bufferLength);
+			var output = new DataView(arrayBuffer);
+			output.setUint32(offset, triangles, true); offset += 4;
+
+			scene.traverse( function ( object ) {
+
+				if ( !(object instanceof THREE.Mesh) ) return;
+				if ( !(object.geometry instanceof THREE.Geometry )) return;
+
+				var geometry = object.geometry;
+				var matrixWorld = object.matrixWorld;
+
+				var vertices = geometry.vertices;
+				var faces = geometry.faces;
+
+				normalMatrixWorld.getNormalMatrix( matrixWorld );
+
+				for ( var i = 0, l = faces.length; i < l; i ++ ) {
+
+					var face = faces[ i ];
+
+					vector.copy( face.normal ).applyMatrix3( normalMatrixWorld ).normalize();
+
+					output.setFloat32(offset, vector.x, true); offset += 4; // normal
+					output.setFloat32(offset, vector.y, true); offset += 4;
+					output.setFloat32(offset, vector.z, true); offset += 4;
+
+					var indices = [ face.a, face.b, face.c ];
+
+					for ( var j = 0; j < 3; j ++ ) {
+
+						vector.copy( vertices[ indices[ j ] ] ).applyMatrix4( matrixWorld );
+
+						output.setFloat32(offset, vector.x, true); offset += 4; // vertices
+						output.setFloat32(offset, vector.y, true); offset += 4;
+						output.setFloat32(offset, vector.z, true); offset += 4;
+
+					}
+
+					output.setUint16(offset, 0, true); offset += 2; // attribute byte count					
+
+				}
+
+			} );
+			
+			return output;
+
+		};
+
+	}() )
+
+};

+ 1 - 1
examples/js/libs/dat.gui.min.js

@@ -87,7 +87,7 @@ b,c){Object.defineProperty(a,b,{get:function(){if(this.__state.space==="RGB")ret
 "HEX")b.__state[c]=a.component_from_hex(b.__state.hex,e);else if(b.__state.space==="HSV")d.extend(b.__state,a.hsv_to_rgb(b.__state.h,b.__state.s,b.__state.v));else throw"Corrupted color state";}function h(b){var c=a.rgb_to_hsv(b.r,b.g,b.b);d.extend(b.__state,{s:c.s,v:c.v});if(d.isNaN(c.h)){if(d.isUndefined(b.__state.h))b.__state.h=0}else b.__state.h=c.h}var j=function(){this.__state=e.apply(this,arguments);if(this.__state===false)throw"Failed to interpret color arguments";this.__state.a=this.__state.a||
 1};j.COMPONENTS="r,g,b,h,s,v,hex,a".split(",");d.extend(j.prototype,{toString:function(){return c(this)},toOriginal:function(){return this.__state.conversion.write(this)}});f(j.prototype,"r",2);f(j.prototype,"g",1);f(j.prototype,"b",0);b(j.prototype,"h");b(j.prototype,"s");b(j.prototype,"v");Object.defineProperty(j.prototype,"a",{get:function(){return this.__state.a},set:function(a){this.__state.a=a}});Object.defineProperty(j.prototype,"hex",{get:function(){if(!this.__state.space!=="HEX")this.__state.hex=
 a.rgb_to_hex(this.r,this.g,this.b);return this.__state.hex},set:function(a){this.__state.space="HEX";this.__state.hex=a}});return j}(dat.color.interpret,dat.color.math=function(){var e;return{hsv_to_rgb:function(a,c,d){var e=a/60-Math.floor(a/60),b=d*(1-c),n=d*(1-e*c),c=d*(1-(1-e)*c),a=[[d,c,b],[n,d,b],[b,d,c],[b,n,d],[c,b,d],[d,b,n]][Math.floor(a/60)%6];return{r:a[0]*255,g:a[1]*255,b:a[2]*255}},rgb_to_hsv:function(a,c,d){var e=Math.min(a,c,d),b=Math.max(a,c,d),e=b-e;if(b==0)return{h:NaN,s:0,v:0};
-a=a==b?(c-d)/e:c==b?2+(d-a)/e:4+(a-c)/e;a/=6;a<0&&(a+=1);return{h:a*360,s:e/b,v:b/255}},rgb_to_hex:function(a,c,d){a=this.hex_with_component(0,2,a);a=this.hex_with_component(a,1,c);return a=this.hex_with_component(a,0,d)},component_from_hex:function(a,c){return a>>c*8&255},hex_with_component:function(a,c,d){return d<<(e=c*8)|a&~(255<<e)}}}(),dat.color.toString,dat.utils.common),dat.color.interpret,dat.utils.common),dat.utils.requestAnimationFrame=function(){return window.webkitRequestAnimationFrame||
+a=a==b?(c-d)/e:c==b?2+(d-a)/e:4+(a-c)/e;a/=6;a<0&&(a+=1);return{h:a*360,s:e/b,v:b/255}},rgb_to_hex:function(a,c,d){a=this.hex_with_component(0,2,a);a=this.hex_with_component(a,1,c);return a=this.hex_with_component(a,0,d)},component_from_hex:function(a,c){return a>>c*8&255},hex_with_component:function(a,c,d){return d<<(e=c*8)|a&~(255<<e)}}}(),dat.color.toString,dat.utils.common),dat.color.interpret,dat.utils.common),dat.utils.requestAnimationFrame=function(){return window.requestAnimationFrame||window.webkitRequestAnimationFrame||
 window.mozRequestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame||function(e){window.setTimeout(e,1E3/60)}}(),dat.dom.CenteredDiv=function(e,a){var c=function(){this.backgroundElement=document.createElement("div");a.extend(this.backgroundElement.style,{backgroundColor:"rgba(0,0,0,0.8)",top:0,left:0,display:"none",zIndex:"1000",opacity:0,WebkitTransition:"opacity 0.2s linear"});e.makeFullscreen(this.backgroundElement);this.backgroundElement.style.position="fixed";this.domElement=
 document.createElement("div");a.extend(this.domElement.style,{position:"fixed",display:"none",zIndex:"1001",opacity:0,WebkitTransition:"-webkit-transform 0.2s ease-out, opacity 0.2s linear"});document.body.appendChild(this.backgroundElement);document.body.appendChild(this.domElement);var c=this;e.bind(this.backgroundElement,"click",function(){c.hide()})};c.prototype.show=function(){var c=this;this.backgroundElement.style.display="block";this.domElement.style.display="block";this.domElement.style.opacity=
 0;this.domElement.style.webkitTransform="scale(1.1)";this.layout();a.defer(function(){c.backgroundElement.style.opacity=1;c.domElement.style.opacity=1;c.domElement.style.webkitTransform="scale(1)"})};c.prototype.hide=function(){var a=this,c=function(){a.domElement.style.display="none";a.backgroundElement.style.display="none";e.unbind(a.domElement,"webkitTransitionEnd",c);e.unbind(a.domElement,"transitionend",c);e.unbind(a.domElement,"oTransitionEnd",c)};e.bind(this.domElement,"webkitTransitionEnd",

+ 600 - 0
examples/js/libs/msgpack-js.js

@@ -0,0 +1,600 @@
+( // Module boilerplate to support browser globals and browserify and AMD.
+  typeof define === "function" ? function (m) { define("msgpack-js", m); } :
+  typeof exports === "object" ? function (m) { module.exports = m(); } :
+  function(m){ this.msgpack = m(); }
+)(function () {
+"use strict";
+
+var exports = {};
+
+exports.inspect = inspect;
+function inspect(buffer) {
+  if (buffer === undefined) return "undefined";
+  var view;
+  var type;
+  if (buffer instanceof ArrayBuffer) {
+    type = "ArrayBuffer";
+    view = new DataView(buffer);
+  }
+  else if (buffer instanceof DataView) {
+    type = "DataView";
+    view = buffer;
+  }
+  if (!view) return JSON.stringify(buffer);
+  var bytes = [];
+  for (var i = 0; i < buffer.byteLength; i++) {
+    if (i > 20) {
+      bytes.push("...");
+      break;
+    }
+    var byte = view.getUint8(i).toString(16);
+    if (byte.length === 1) byte = "0" + byte;
+    bytes.push(byte);
+  }
+  return "<" + type + " " + bytes.join(" ") + ">";
+}
+
+// Encode string as utf8 into dataview at offset
+exports.utf8Write = utf8Write;
+function utf8Write(view, offset, string) {
+  var byteLength = view.byteLength;
+  for(var i = 0, l = string.length; i < l; i++) {
+    var codePoint = string.charCodeAt(i);
+
+    // One byte of UTF-8
+    if (codePoint < 0x80) {
+      view.setUint8(offset++, codePoint >>> 0 & 0x7f | 0x00);
+      continue;
+    }
+
+    // Two bytes of UTF-8
+    if (codePoint < 0x800) {
+      view.setUint8(offset++, codePoint >>> 6 & 0x1f | 0xc0);
+      view.setUint8(offset++, codePoint >>> 0 & 0x3f | 0x80);
+      continue;
+    }
+
+    // Three bytes of UTF-8.  
+    if (codePoint < 0x10000) {
+      view.setUint8(offset++, codePoint >>> 12 & 0x0f | 0xe0);
+      view.setUint8(offset++, codePoint >>> 6  & 0x3f | 0x80);
+      view.setUint8(offset++, codePoint >>> 0  & 0x3f | 0x80);
+      continue;
+    }
+
+    // Four bytes of UTF-8
+    if (codePoint < 0x110000) {
+      view.setUint8(offset++, codePoint >>> 18 & 0x07 | 0xf0);
+      view.setUint8(offset++, codePoint >>> 12 & 0x3f | 0x80);
+      view.setUint8(offset++, codePoint >>> 6  & 0x3f | 0x80);
+      view.setUint8(offset++, codePoint >>> 0  & 0x3f | 0x80);
+      continue;
+    }
+    throw new Error("bad codepoint " + codePoint);
+  }
+}
+
+exports.utf8Read = utf8Read;
+function utf8Read(view, offset, length) {
+  var string = "";
+  for (var i = offset, end = offset + length; i < end; i++) {
+    var byte = view.getUint8(i);
+    // One byte character
+    if ((byte & 0x80) === 0x00) {
+      string += String.fromCharCode(byte);
+      continue;
+    }
+    // Two byte character
+    if ((byte & 0xe0) === 0xc0) {
+      string += String.fromCharCode(
+        ((byte & 0x0f) << 6) | 
+        (view.getUint8(++i) & 0x3f)
+      );
+      continue;
+    }
+    // Three byte character
+    if ((byte & 0xf0) === 0xe0) {
+      string += String.fromCharCode(
+        ((byte & 0x0f) << 12) |
+        ((view.getUint8(++i) & 0x3f) << 6) |
+        ((view.getUint8(++i) & 0x3f) << 0)
+      );
+      continue;
+    }
+    // Four byte character
+    if ((byte & 0xf8) === 0xf0) {
+      string += String.fromCharCode(
+        ((byte & 0x07) << 18) |
+        ((view.getUint8(++i) & 0x3f) << 12) |
+        ((view.getUint8(++i) & 0x3f) << 6) |
+        ((view.getUint8(++i) & 0x3f) << 0)
+      );
+      continue;
+    }
+    throw new Error("Invalid byte " + byte.toString(16));
+  }
+  return string;
+}
+
+exports.utf8ByteCount = utf8ByteCount;
+function utf8ByteCount(string) {
+  var count = 0;
+  for(var i = 0, l = string.length; i < l; i++) {
+    var codePoint = string.charCodeAt(i);
+    if (codePoint < 0x80) {
+      count += 1;
+      continue;
+    }
+    if (codePoint < 0x800) {
+      count += 2;
+      continue;
+    }
+    if (codePoint < 0x10000) {
+      count += 3;
+      continue;
+    }
+    if (codePoint < 0x110000) {
+      count += 4;
+      continue;
+    }
+    throw new Error("bad codepoint " + codePoint);
+  }
+  return count;
+}
+
+exports.encode = function (value) {
+  var buffer = new ArrayBuffer(sizeof(value));
+  var view = new DataView(buffer);
+  encode(value, view, 0);
+  return buffer;
+}
+
+exports.decode = decode;
+
+// http://wiki.msgpack.org/display/MSGPACK/Format+specification
+// I've extended the protocol to have two new types that were previously reserved.
+//   buffer 16  11011000  0xd8
+//   buffer 32  11011001  0xd9
+// These work just like raw16 and raw32 except they are node buffers instead of strings.
+//
+// Also I've added a type for `undefined`
+//   undefined  11000100  0xc4
+
+function Decoder(view, offset) {
+  this.offset = offset || 0;
+  this.view = view;
+}
+Decoder.prototype.map = function (length) {
+  var value = {};
+  for (var i = 0; i < length; i++) {
+    var key = this.parse();
+    value[key] = this.parse();
+  }
+  return value;
+};
+Decoder.prototype.buf = function (length) {
+  var value = new ArrayBuffer(length);
+  (new Uint8Array(value)).set(new Uint8Array(this.view.buffer, this.offset, length), 0);
+  this.offset += length;
+  return value;
+};
+Decoder.prototype.raw = function (length) {
+  var value = utf8Read(this.view, this.offset, length);
+  this.offset += length;
+  return value;
+};
+Decoder.prototype.array = function (length) {
+  var value = new Array(length);
+  for (var i = 0; i < length; i++) {
+    value[i] = this.parse();
+  }
+  return value;
+};
+Decoder.prototype.parse = function () {
+  var type = this.view.getUint8(this.offset);
+  var value, length;
+  // FixRaw
+  if ((type & 0xe0) === 0xa0) {
+    length = type & 0x1f;
+    this.offset++;
+    return this.raw(length);
+  }
+  // FixMap
+  if ((type & 0xf0) === 0x80) {
+    length = type & 0x0f;
+    this.offset++;
+    return this.map(length);
+  }
+  // FixArray
+  if ((type & 0xf0) === 0x90) {
+    length = type & 0x0f;
+    this.offset++;
+    return this.array(length);
+  }
+  // Positive FixNum
+  if ((type & 0x80) === 0x00) {
+    this.offset++;
+    return type;
+  }
+  // Negative Fixnum
+  if ((type & 0xe0) === 0xe0) {
+    value = this.view.getInt8(this.offset);
+    this.offset++;
+    return value;
+  }
+  switch (type) {
+  // raw 16
+  case 0xda:
+    length = this.view.getUint16(this.offset + 1);
+    this.offset += 3;
+    return this.raw(length);
+  // raw 32
+  case 0xdb:
+    length = this.view.getUint32(this.offset + 1);
+    this.offset += 5;
+    return this.raw(length);
+  // nil
+  case 0xc0:
+    this.offset++;
+    return null;
+  // false
+  case 0xc2:
+    this.offset++;
+    return false;
+  // true
+  case 0xc3:
+    this.offset++;
+    return true;
+  // undefined
+  case 0xc4:
+    this.offset++;
+    return undefined;
+  // uint8
+  case 0xcc:
+    value = this.view.getUint8(this.offset + 1);
+    this.offset += 2;
+    return value;
+  // uint 16
+  case 0xcd:
+    value = this.view.getUint16(this.offset + 1);
+    this.offset += 3;
+    return value;
+  // uint 32
+  case 0xce:
+    value = this.view.getUint32(this.offset + 1);
+    this.offset += 5;
+    return value;
+  // int 8
+  case 0xd0:
+    value = this.view.getInt8(this.offset + 1);
+    this.offset += 2;
+    return value;
+  // int 16
+  case 0xd1:
+    value = this.view.getInt16(this.offset + 1);
+    this.offset += 3;
+    return value;
+  // int 32
+  case 0xd2:
+    value = this.view.getInt32(this.offset + 1);
+    this.offset += 5;
+    return value;
+  // map 16
+  case 0xde:
+    length = this.view.getUint16(this.offset + 1);
+    this.offset += 3;
+    return this.map(length);
+  // map 32
+  case 0xdf:
+    length = this.view.getUint32(this.offset + 1);
+    this.offset += 5;
+    return this.map(length);
+  // array 16
+  case 0xdc:
+    length = this.view.getUint16(this.offset + 1);
+    this.offset += 3;
+    return this.array(length);
+  // array 32
+  case 0xdd:
+    length = this.view.getUint32(this.offset + 1);
+    this.offset += 5;
+    return this.array(length);
+  // buffer 16
+  case 0xd8:
+    length = this.view.getUint16(this.offset + 1);
+    this.offset += 3;
+    return this.buf(length);
+  // buffer 32
+  case 0xd9:
+    length = this.view.getUint32(this.offset + 1);
+    this.offset += 5;
+    return this.buf(length);
+  // float
+  case 0xca:
+    value = this.view.getFloat32(this.offset + 1);
+    this.offset += 5;
+    return value;
+  // double
+  case 0xcb:
+    value = this.view.getFloat64(this.offset + 1);
+    this.offset += 9;
+    return value;
+  }
+  throw new Error("Unknown type 0x" + type.toString(16));
+};
+function decode(buffer) {
+  var view = new DataView(buffer);
+  var decoder = new Decoder(view);
+  var value = decoder.parse();
+  if (decoder.offset !== buffer.byteLength) throw new Error((buffer.byteLength - decoder.offset) + " trailing bytes");
+  return value;
+}
+
+function encode(value, view, offset) {
+  var type = typeof value;
+
+  // Strings Bytes
+  if (type === "string") {
+    var length = utf8ByteCount(value);
+    // fix raw
+    if (length < 0x20) {
+      view.setUint8(offset, length | 0xa0);
+      utf8Write(view, offset + 1, value);
+      return 1 + length;
+    }
+    // raw 16
+    if (length < 0x10000) {
+      view.setUint8(offset, 0xda);
+      view.setUint16(offset + 1, length);
+      utf8Write(view, offset + 3, value);
+      return 3 + length;
+    }
+    // raw 32
+    if (length < 0x100000000) {
+      view.setUint8(offset, 0xdb);
+      view.setUint32(offset + 1, length);
+      utf8Write(view, offset + 5, value);
+      return 5 + length;
+    }
+  }
+
+  if (value instanceof ArrayBuffer) {
+    var length = value.byteLength;
+    // buffer 16
+    if (length < 0x10000) {
+      view.setUint8(offset, 0xd8);
+      view.setUint16(offset + 1, length);
+      (new Uint8Array(view.buffer)).set(new Uint8Array(value), offset + 3);
+      return 3 + length;
+    }
+    // buffer 32
+    if (length < 0x100000000) {
+      view.setUint8(offset, 0xd9);
+      view.setUint32(offset + 1, length);
+      (new Uint8Array(view.buffer)).set(new Uint8Array(value), offset + 5);
+      return 5 + length;
+    }
+  }
+  
+  if (type === "number") {
+    // Floating Point
+    if ((value << 0) !== value) {
+      view.setUint8(offset, 0xcb);
+      view.setFloat64(offset + 1, value);
+      return 9;
+    }
+
+    // Integers
+    if (value >=0) {
+      // positive fixnum
+      if (value < 0x80) {
+        view.setUint8(offset, value);
+        return 1;
+      }
+      // uint 8
+      if (value < 0x100) {
+        view.setUint8(offset, 0xcc);
+        view.setUint8(offset + 1, value);
+        return 2;
+      }
+      // uint 16
+      if (value < 0x10000) {
+        view.setUint8(offset, 0xcd);
+        view.setUint16(offset + 1, value);
+        return 3;
+      }
+      // uint 32
+      if (value < 0x100000000) {
+        view.setUint8(offset, 0xce);
+        view.setUint32(offset + 1, value);
+        return 5;
+      }
+      throw new Error("Number too big 0x" + value.toString(16));
+    }
+    // negative fixnum
+    if (value >= -0x20) {
+      view.setInt8(offset, value);
+      return 1;
+    }
+    // int 8
+    if (value >= -0x80) {
+      view.setUint8(offset, 0xd0);
+      view.setInt8(offset + 1, value);
+      return 2;
+    }
+    // int 16
+    if (value >= -0x8000) {
+      view.setUint8(offset, 0xd1);
+      view.setInt16(offset + 1, value);
+      return 3;
+    }
+    // int 32
+    if (value >= -0x80000000) {
+      view.setUint8(offset, 0xd2);
+      view.setInt32(offset + 1, value);
+      return 5;
+    }
+    throw new Error("Number too small -0x" + (-value).toString(16).substr(1));
+  }
+  
+  // undefined
+  if (type === "undefined") {
+    view.setUint8(offset, 0xc4);
+    return 1;
+  }
+  
+  // null
+  if (value === null) {
+    view.setUint8(offset, 0xc0);
+    return 1;
+  }
+
+  // Boolean
+  if (type === "boolean") {
+    view.setUint8(offset, value ? 0xc3 : 0xc2);
+    return 1;
+  }
+  
+  // Container Types
+  if (type === "object") {
+    var length, size = 0;
+    var isArray = Array.isArray(value);
+
+    if (isArray) {
+      length = value.length;
+    }
+    else {
+      var keys = Object.keys(value);
+      length = keys.length;
+    }
+
+    var size;
+    if (length < 0x10) {
+      view.setUint8(offset, length | (isArray ? 0x90 : 0x80));
+      size = 1;
+    }
+    else if (length < 0x10000) {
+      view.setUint8(offset, isArray ? 0xdc : 0xde);
+      view.setUint16(offset + 1, length);
+      size = 3;
+    }
+    else if (length < 0x100000000) {
+      view.setUint8(offset, isArray ? 0xdd : 0xdf);
+      view.setUint32(offset + 1, length);
+      size = 5;
+    }
+
+    if (isArray) {
+      for (var i = 0; i < length; i++) {
+        size += encode(value[i], view, offset + size);
+      }
+    }
+    else {
+      for (var i = 0; i < length; i++) {
+        var key = keys[i];
+        size += encode(key, view, offset + size);
+        size += encode(value[key], view, offset + size);
+      }
+    }
+    
+    return size;
+  }
+  throw new Error("Unknown type " + type);
+}
+
+function sizeof(value) {
+  var type = typeof value;
+
+  // Raw Bytes
+  if (type === "string") {
+    var length = utf8ByteCount(value);
+    if (length < 0x20) {
+      return 1 + length;
+    }
+    if (length < 0x10000) {
+      return 3 + length;
+    }
+    if (length < 0x100000000) {
+      return 5 + length;
+    }
+  }
+  
+  if (value instanceof ArrayBuffer) {
+    var length = value.byteLength;
+    if (length < 0x10000) {
+      return 3 + length;
+    }
+    if (length < 0x100000000) {
+      return 5 + length;
+    }
+  }
+  
+  if (type === "number") {
+    // Floating Point
+    // double
+    if (value << 0 !== value) return 9;
+
+    // Integers
+    if (value >=0) {
+      // positive fixnum
+      if (value < 0x80) return 1;
+      // uint 8
+      if (value < 0x100) return 2;
+      // uint 16
+      if (value < 0x10000) return 3;
+      // uint 32
+      if (value < 0x100000000) return 5;
+      // uint 64
+      if (value < 0x10000000000000000) return 9;
+      throw new Error("Number too big 0x" + value.toString(16));
+    }
+    // negative fixnum
+    if (value >= -0x20) return 1;
+    // int 8
+    if (value >= -0x80) return 2;
+    // int 16
+    if (value >= -0x8000) return 3;
+    // int 32
+    if (value >= -0x80000000) return 5;
+    // int 64
+    if (value >= -0x8000000000000000) return 9;
+    throw new Error("Number too small -0x" + value.toString(16).substr(1));
+  }
+  
+  // Boolean, null, undefined
+  if (type === "boolean" || type === "undefined" || value === null) return 1;
+  
+  // Container Types
+  if (type === "object") {
+    var length, size = 0;
+    if (Array.isArray(value)) {
+      length = value.length;
+      for (var i = 0; i < length; i++) {
+        size += sizeof(value[i]);
+      }
+    }
+    else {
+      var keys = Object.keys(value);
+      length = keys.length;
+      for (var i = 0; i < length; i++) {
+        var key = keys[i];
+        size += sizeof(key) + sizeof(value[key]);
+      }
+    }
+    if (length < 0x10) {
+      return 1 + size;
+    }
+    if (length < 0x10000) {
+      return 3 + size;
+    }
+    if (length < 0x100000000) {
+      return 5 + size;
+    }
+    throw new Error("Array or object too long 0x" + length.toString(16));
+  }
+  throw new Error("Unknown type " + type);
+}
+
+return exports;
+
+});

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

@@ -0,0 +1,32 @@
+// pnltri.js / raw.github.com/jahting/pnltri.js/master/LICENSE
+'use strict';var PNLTRI={REVISION:"1.4"};PNLTRI.Math={log2:function(a){return Math.log(a)/Math.LN2},random:Math.random,array_shuffle:function(a){for(var c=a.length-1;0<c;c--){var b=Math.floor(PNLTRI.Math.random()*(c+1)),d=a[c];a[c]=a[b];a[b]=d}return a},ptsCrossProd:function(a,c,b){return(c.x-a.x)*(b.y-a.y)-(c.y-a.y)*(b.x-a.x)}};PNLTRI.Math.EPSILON_P=Math.pow(2,-43);PNLTRI.Math.EPSILON_N=-PNLTRI.Math.EPSILON_P;PNLTRI.PolygonData=function(a){this.vertices=[];this.segments=[];this.idNextPolyChain=0;this.PolyLeftArr=[];this.monoSubPolyChains=[];this.triangles=[];if(a)for(var c=0,b=a.length;c<b;c++)this.addPolygonChain(a[c])};
+PNLTRI.PolygonData.prototype={constructor:PNLTRI.PolygonData,getSegments:function(){return this.segments},getFirstSegment:function(){return this.segments[0]},getMonoSubPolys:function(){return this.monoSubPolyChains},getTriangles:function(){return this.triangles.concat()},nbPolyChains:function(){return this.idNextPolyChain},get_PolyLeftArr:function(){return this.PolyLeftArr.concat()},set_PolyLeft_wrong:function(a){this.PolyLeftArr[a]=!1},compare_pts_yx:function(a,c){var b=a.y-c.y;if(b<PNLTRI.Math.EPSILON_N)return-1;
+if(b>PNLTRI.Math.EPSILON_P)return 1;b=a.x-c.x;return b<PNLTRI.Math.EPSILON_N?-1:b>PNLTRI.Math.EPSILON_P?1:0},isClockWise:function(a){var c=a,b=0;do b+=(c.vFrom.x-c.vTo.x)*(c.vFrom.y+c.vTo.y),c=c.snext;while(c!=a);return 0>b},appendVertexEntry:function(a,c){var b={id:this.vertices.length,x:a,y:c,outSegs:[]};this.vertices.push(b);return b},createSegmentEntry:function(a,c){return{chainId:this.idNextPolyChain,vFrom:a,vTo:c,upward:1==this.compare_pts_yx(c,a),sprev:null,snext:null,trLeft:null,trRight:null,
+mprev:null,mnext:null,marked:!1}},appendSegmentEntry:function(a){this.segments.push(a);return a},addVertexChain:function(a){function c(a,b){return Math.abs(a.x-b.x)<PNLTRI.Math.EPSILON_P&&Math.abs(a.y-b.y)<PNLTRI.Math.EPSILON_P}for(var b=[],d,e,k,f=0;f<a.length;f++)d=this.appendVertexEntry(a[f].x,a[f].y),e=!0,k=b.length-1,0<=k&&c(d,b[k])&&(e=!1),e&&b.push(d);1<b.length&&c(b[b.length-1],b[0])&&b.pop();return b},addPolygonChain:function(a){a=this.addVertexChain(a);if(3>a.length)return console.log("Polygon has < 3 vertices!",
+a),0;for(var c=this.segments.length,b,d,e,k=0;k<a.length-1;k++)b=this.createSegmentEntry(a[k],a[k+1]),e?(b.sprev=e,e.snext=b):d=b,e=b,this.appendSegmentEntry(b);b=this.createSegmentEntry(a[a.length-1],a[0]);b.sprev=e;e.snext=b;this.appendSegmentEntry(b);d.sprev=b;b.snext=d;this.PolyLeftArr[this.idNextPolyChain++]=!0;return this.segments.length-c},initMonoChains:function(){for(var a,c=0;c<this.segments.length;c++)a=this.segments[c],this.PolyLeftArr[a.chainId]?(a.mprev=a.sprev,a.mnext=a.snext,a.vFrom.outSegs.push({segOut:a,
+vertTo:a.vTo})):(a=a.snext,a.mprev=a.snext,a.mnext=a.sprev,a.vFrom.outSegs.push({segOut:a,vertTo:this.segments[c].vFrom}))},createMonoSegment:function(a){this.appendSegmentEntry(a);a.vFrom.outSegs.push({segOut:a,vertTo:a.vTo});return a},newMonoChain:function(a){var c=this.monoSubPolyChains.length;this.monoSubPolyChains[c]=a;return c},splitPolygonChain:function(a,c,b,d){function e(a,b){for(var c,d,e=null,k=4,h=0;h<a.outSegs.length;h++){c=a.outSegs[h];var f=c.vertTo;d=f.x-a.x;var f=f.y-a.y,l=b.x-a.x,
+t=b.y-a.y,v=(d*l+f*t)/Math.sqrt(d*d+f*f)/Math.sqrt(l*l+t*t);(d=0<=d*t-l*f?1-v:3+v)<k&&(k=d,e=c)}return e}var k,f;k=e(c,b);f=e(b,c);k=k.segOut;var l=f.segOut;f=this.createMonoSegment({vFrom:c,vTo:b,upward:!0,mprev:k.mprev,mnext:l});c=this.createMonoSegment({vFrom:b,vTo:c,upward:!1,mprev:l.mprev,mnext:k});k.mprev.mnext=f;l.mprev.mnext=c;k.mprev=c;l.mprev=f;b=this.monoSubPolyChains.length;d?(this.monoSubPolyChains[a]=f,this.monoSubPolyChains[b]=c):(this.monoSubPolyChains[a]=c,this.monoSubPolyChains[b]=
+f);return b},unique_monotone_chains_max:function(){for(var a,c,b,d,e,k=[],f=0;f<this.monoSubPolyChains.length;f++){a=c=this.monoSubPolyChains[f];d=e=a.vFrom;a.marked=!0;a=a.mnext;for(var l=!1;(b=a.vFrom)!=d;){if(a.marked){l=!0;break}else a.marked=!0;1==this.compare_pts_yx(b,e)&&(e=b,c=a);a=a.mnext}l||k.push(c)}return k},normalize_monotone_chains:function(){this.monoSubPolyChains=this.unique_monotone_chains_max();return this.monoSubPolyChains.length},clearTriangles:function(){this.triangles=[]},addTriangle:function(a,
+c,b){this.triangles.push([a.id,c.id,b.id])}};PNLTRI.EarClipTriangulator=function(a){this.polyData=a};
+PNLTRI.EarClipTriangulator.prototype={constructor:PNLTRI.EarClipTriangulator,triangulate_polygon_no_holes:function(){var a=this.polyData,c=a.getFirstSegment(),b=c;if(a.isClockWise(c)){do b.mprev=b.snext,b=b.mnext=b.sprev;while(b!=c);a.set_PolyLeft_wrong(0)}else{do b.mprev=b.sprev,b=b.mnext=b.snext;while(b!=c)}for(c=a=c;a.mnext!=a.mprev;){a:{var b=a.mprev.vFrom.x,d=a.mprev.vFrom.y,e=a.vFrom.x,k=a.vFrom.y,f=a.mnext.vFrom.x,l=a.mnext.vFrom.y,n=f-e,p=l-k,q=b-f,u=d-l,g=e-b,m=k-d;if(PNLTRI.Math.EPSILON_P>
+g*p-n*m)b=!1;else{for(var h=a.mprev.mprev,s=a.mnext;s!=h;){var s=s.mnext,r=s.vFrom.x,t=s.vFrom.y,v=r-b,w=t-d;if(0!=v||0!=w){var x=r-e,y=t-k;if(0!=x||0!=y)if(r-=f,t-=l,(0!=r||0!=t)&&n*y-p*x>=PNLTRI.Math.EPSILON_N&&g*w-m*v>=PNLTRI.Math.EPSILON_N&&q*t-u*r>=PNLTRI.Math.EPSILON_N){b=!1;break a}}}b=!0}}if(b)this.polyData.addTriangle(a.mprev.vFrom,a.vFrom,a.mnext.vFrom),a.mprev.mnext=a.mnext,a.mnext.mprev=a.mprev,c=a=a.mnext;else if(a=a.mnext,a==c)return!1}return!0}};PNLTRI.Trapezoid=function(a,c,b,d){this.vHigh=a?a:{x:Number.POSITIVE_INFINITY,y:Number.POSITIVE_INFINITY};this.vLow=c?c:{x:Number.NEGATIVE_INFINITY,y:Number.NEGATIVE_INFINITY};this.lseg=b;this.rseg=d;this.depth=-1;this.monoDone=!1};
+PNLTRI.Trapezoid.prototype={constructor:PNLTRI.Trapezoid,clone:function(){var a=new PNLTRI.Trapezoid(this.vHigh,this.vLow,this.lseg,this.rseg);a.uL=this.uL;a.uR=this.uR;a.dL=this.dL;a.dR=this.dR;a.sink=this.sink;return a},splitOffLower:function(a){var c=this.clone();this.vLow=c.vHigh=a;this.dL=c;c.uL=this;this.dR=c.uR=null;c.dL&&(c.dL.uL=c);c.dR&&(c.dR.uR=c);return c}};PNLTRI.QsNode=function(a){this.trap=a;a.sink=this};PNLTRI.QsNode.prototype={constructor:PNLTRI.QsNode};
+PNLTRI.QueryStructure=function(a){var c=new PNLTRI.Trapezoid(null,null,null,null);this.trapArray=[];this.appendTrapEntry(c);this.root=new PNLTRI.QsNode(c);if(a){for(var c=a.getSegments(),b=0;b<c.length;b++)c[b].rootFrom=c[b].rootTo=this.root,c[b].is_inserted=!1;this.compare_pts_yx=a.compare_pts_yx}else this.compare_pts_yx=PNLTRI.PolygonData.prototype.compare_pts_yx};
+PNLTRI.QueryStructure.prototype={constructor:PNLTRI.QueryStructure,getRoot:function(){return this.root},appendTrapEntry:function(a){a.trapID=this.trapArray.length;this.trapArray.push(a)},cloneTrap:function(a){a=a.clone();this.appendTrapEntry(a);return a},splitNodeAtPoint:function(a,c,b){var d=a.trap;if(d.vHigh==c||d.vLow==c)return a;var e=d.splitOffLower(c);this.appendTrapEntry(e);a.yval=c;a.trap=null;a.right=new PNLTRI.QsNode(d);a.left=new PNLTRI.QsNode(e);return b?d.sink:e.sink},fpEqual:function(a,
+c){return Math.abs(a-c)<PNLTRI.Math.EPSILON_P},is_left_of:function(a,c,b){b=a.vFrom.x-c.x;var d=a.vTo.x-c.x;if(Math.abs(a.vTo.y-c.y)<PNLTRI.Math.EPSILON_P)a=d,c=b;else if(Math.abs(a.vFrom.y-c.y)<PNLTRI.Math.EPSILON_P)a=b,c=d;else return a.upward?PNLTRI.Math.ptsCrossProd(a.vFrom,a.vTo,c):PNLTRI.Math.ptsCrossProd(a.vTo,a.vFrom,c);return Math.abs(a)<PNLTRI.Math.EPSILON_P?Math.abs(c)<PNLTRI.Math.EPSILON_P?0:c:a},ptNode:function(a,c,b){for(var d;b;)if(b.yval)d=a==b.yval?c:a,d=this.compare_pts_yx(d,b.yval),
+b=-1==d?b.left:b.right;else if(b.seg)a==b.seg.vFrom||a==b.seg.vTo?this.fpEqual(a.y,c.y)?b=c.x<a.x?b.left:b.right:(d=this.is_left_of(b.seg,c,!1),b=0<d?b.left:0>d?b.right:a==b.seg.vFrom?b.right:b.left):(d=a,d=this.is_left_of(b.seg,d,!0),b=0<d?b.left:b.right);else return b.trap||console.log("ptNode: unknown type",b),b},add_segment:function(a){function c(){var a=g.uL||g.uR;a.dL&&a.dR?g==a.dL?(h.uL=null,a.dL=m):(m.uR=null,a.dR=h):(h.uL=null,h.uR=a,a.dR=h)}function b(a){g.vLow==u.vLow?(q?g.dL?(a.uL=m,m.dL=
+a,h.dR=null):(a.uR=h,m.dL=null,h.dR=a):(a.uL=m,a.uR=h,m.dL=h.dR=a),m.dR=h.dL=null):(a.uL&&a.uR&&(a.uL==g?(a.usave=a.uR,a.uleft=!0):(a.usave=a.uL,a.uleft=!1)),a.uL=m,a.uR=h,m.dR=h.dL=a,m.dL=h.dR=null)}function d(){var b;g.vLow==u.vLow&&q?(g.dL.uL=m,g.dR.uR=h,m.dL=g.dL,h.dR=g.dR,b=m.dR=h.dL=null):(g.dL.uL=m,g.dR.uR=h,0<e.is_left_of(a,g.vLow,!0)?(b=g.dR,g.dR.uL=m,m.dL=g.dL,h.dR=null):(b=g.dL,g.dL.uR=h,h.dR=g.dR,m.dL=null),m.dR=h.dL=b);return b}var e=this,k,f,l,n,p,q;a.upward?(n=a.vFrom,k=a.vTo,p=a.rootFrom,
+f=a.rootTo,q=a.sprev.is_inserted,l=a.snext.is_inserted):(n=a.vTo,k=a.vFrom,p=a.rootTo,f=a.rootFrom,q=a.snext.is_inserted,l=a.sprev.is_inserted);f=this.ptNode(k,n,f);l||(f=this.splitNodeAtPoint(f,k,!1));l=f.trap;if(l.uL||l.uR){f=this.ptNode(n,k,p);q||(f=this.splitNodeAtPoint(f,n,!0));var u=f.trap,g=l,m,h,s,r;for(k=this.trapArray.length+2;g;){if(0>--k){console.log("ERR add_segment: infinite loop",g,a,this);return}if(!g.dL&&!g.dR){console.log("ERR add_segment: missing successors",g,a,this);return}n=
+g.sink;n.seg=a;n.trap=null;r&&r.rseg==g.rseg?(m=g,h=r,h.vLow=g.vLow,n.left=new PNLTRI.QsNode(m),n.right=r.sink):(s&&s.lseg==g.lseg?(h=g,m=s,m.vLow=g.vLow,n.left=s.sink):(m=g,h=this.cloneTrap(g),n.left=new PNLTRI.QsNode(m)),n.right=new PNLTRI.QsNode(h));g.uL&&g.uR?g.usave?(g.uleft?(h.uL=g.uR,h.uR=g.usave,h.uL.dL=h,h.uR.dR=h):(m.uR=g.uL,m.uL=g.usave,m.uL.dL=m,m.uR.dR=m),m.usave=h.usave=null):g.vHigh==l.vHigh?(h.uR.dR=h,m.uR=h.uL=null):h==g?(h.uL=h.uR,h.uR=null,h.uL.dL=h):(m.uR=m.uL,m.uL=null):c();g.dL&&
+g.dR?n=d():(n=g.dL?g.dL:g.dR,b(n));m.rseg&&(m.rseg.trLeft=h);h.lseg&&(h.lseg.trRight=m);m.rseg=h.lseg=a;a.trLeft=m;a.trRight=h;g.vLow!=u.vLow?(s=m,r=h,g=n):g=null}a.is_inserted=!0}else console.log("ERR add_segment: missing trFirst.uX: ",l)},assignDepths:function(a){var c=[this.trapArray[0]],b=[],d,e,k=0;do{for(var f=1==k%2;d=c.pop();)-1==d.depth&&(d.depth=k,d.uL&&c.push(d.uL),d.uR&&c.push(d.uR),d.dL&&c.push(d.dL),d.dR&&c.push(d.dR),(e=d.lseg)&&-1==e.trLeft.depth&&b.push(e.trLeft),e=d.rseg)&&(-1==
+e.trRight.depth&&b.push(e.trRight),e.upward!=f&&a.set_PolyLeft_wrong(e.chainId));c=b;b=[];k++}while(0<c.length)},find_first_inside:function(){for(var a,c=0,b=this.trapArray.length;c<b;c++)if(a=this.trapArray[c],1==a.depth%2&&!a.monoDone&&(!a.uL&&!a.uR||!a.dL&&!a.dR)&&a.lseg)return a;return null}};PNLTRI.Trapezoider=function(a){this.polyData=a;this.queryStructure=new PNLTRI.QueryStructure(this.polyData)};
+PNLTRI.Trapezoider.prototype={constructor:PNLTRI.Trapezoider,find_first_inside:function(){return this.queryStructure.find_first_inside()},math_logstar_n:function(a){var c;for(c=0;1<=a;c++)a=PNLTRI.Math.log2(a);return c-1},math_NH:function(a,c){var b,d;b=0;for(d=a;b<c;b++)d=PNLTRI.Math.log2(d);return Math.ceil(1*a/d)-1},optimise_randomlist:function(a){var c=0,b=this.polyData.nbPolyChains();if(1!=b)for(var d=Array(b),e=a.concat(),k=0;k<e.length;k++){var f=e[k].chainId;d[f]?a[b++]=e[k]:(a[c++]=e[k],
+d[f]=!0)}},trapezoide_polygon:function(){var a=this.polyData.getSegments().concat();PNLTRI.Math.array_shuffle(a);this.optimise_randomlist(a);var c=a.length,b=this.queryStructure,d,e,k,f=this.math_logstar_n(c);for(k=1;k<=f;k++){e=this.math_NH(c,k);for(d=this.math_NH(c,k-1);d<e;d++)b.add_segment(a[d]);for(d=e;d<c;d++)e=a[d],e.rootFrom=this.queryStructure.ptNode(e.vFrom,e.vTo,e.rootFrom),e.rootTo=this.queryStructure.ptNode(e.vTo,e.vFrom,e.rootTo)}for(d=this.math_NH(c,f);d<c;d++)b.add_segment(a[d]);b.assignDepths(this.polyData);
+for(d=0;d<c;d++)a[d].trLeft=null,a[d].trRight=null}};PNLTRI.MonoSplitter=function(a){this.polyData=a;this.startTrap=this.trapezoider=null};
+PNLTRI.MonoSplitter.prototype={constructor:PNLTRI.MonoSplitter,monotonate_trapezoids:function(){this.trapezoider=new PNLTRI.Trapezoider(this.polyData);this.trapezoider.trapezoide_polygon();this.startTrap=this.trapezoider.find_first_inside();this.polyData.initMonoChains();for(var a=this.startTrap;a;)this.alyTrap(this.polyData.newMonoChain(a.lseg),a,null,null,null),a=this.trapezoider.find_first_inside();return this.polyData.normalize_monotone_chains()},doSplit:function(a,c,b,d){return this.polyData.splitPolygonChain(a,
+c,b,d)},alyTrap:function(a,c,b,d,e){function k(){var a;return(a=f.pop())?(l=a[0],n=a[1],p=a[2],q=a[3],!0):!1}var f=[],l,n,p,q,u;null==b&&(d=!0,c.uL?b=!0:c.dL?b=!1:(d=!1,b=c.uR?!0:!1));for(c&&f.push([c,b,d,a]);k();)if(!l.monoDone){l.monoDone=!0;if(!l.lseg||!l.rseg)return console.log("ERR alyTrap: lseg/rseg missing",l),f;n?p?(a=l.uL,c=l.uR,b=l.dL,d=l.dR):(a=l.uR,c=l.uL,b=l.dR,d=l.dL):p?(a=l.dL,c=l.dR,b=l.uL,d=l.uR):(a=l.dR,c=l.dL,b=l.uR,d=l.uL);if(c||d)u=this.doSplit(q,l.vLow,l.vHigh,p);d&&f.push([d,
+n,!p,u]);c&&f.push([c,!n,!p,u]);b&&f.push([b,n,p,q]);b||d||a&&f.push([a,!n,p,q]);if(e)return f}return[]}};PNLTRI.MonoTriangulator=function(a){this.polyData=a};
+PNLTRI.MonoTriangulator.prototype={constructor:PNLTRI.MonoTriangulator,triangulate_all_polygons:function(){var a=this.polyData.getMonoSubPolys();this.polyData.clearTriangles();for(var c=0;c<a.length;c++){var b=a[c],d=b.mprev,e=b.mnext;e.mnext==d?this.polyData.addTriangle(b.vFrom,e.vFrom,d.vFrom):this.triangulate_monotone_polygon(b)}},triangulate_monotone_polygon:function(a){var c=a.mnext;a=a.vFrom;var b=[c.vFrom],d=0,c=c.mnext,e=c.vFrom;if(e!=a){for(;e!=a||1<d;)if(0<d)if(0<PNLTRI.Math.ptsCrossProd(e,
+b[d-1],b[d]))this.polyData.addTriangle(b[d-1],b[d],e),d--;else if(b[++d]=e,e==a)for(console.log("ERR uni-y-monotone: only concave angles left",b);1<d;)d--,this.polyData.addTriangle(b[d-1],b[d],b[d+1]);else c=c.mnext,e=c.vFrom;else b[++d]=e,c=c.mnext,e=c.vFrom;this.polyData.addTriangle(b[d-1],b[d],e)}}};PNLTRI.Triangulator=function(){this.lastPolyData=null};
+PNLTRI.Triangulator.prototype={constructor:PNLTRI.Triangulator,clear_lastData:function(){this.lastPolyData=null},get_PolyLeftArr:function(){return this.lastPolyData?this.lastPolyData.get_PolyLeftArr():null},triangulate_polygon:function(a,c){this.clear_lastData();if(!a||0==a.length)return[];var b=new PNLTRI.PolygonData(a),d;d=c?!1:1==b.nbPolyChains();d&&(d=new PNLTRI.EarClipTriangulator(b),d=d.triangulate_polygon_no_holes());if(!d){(new PNLTRI.MonoSplitter(b)).monotonate_trapezoids();d=new PNLTRI.MonoTriangulator(b);
+d.triangulate_all_polygons();d=b.getSegments();for(var e=0;e<d.length;e++)d[e].vFrom.outSegs=null}this.lastPolyData=b;return b.getTriangles()}};

+ 36 - 0
examples/js/libs/require.js

@@ -0,0 +1,36 @@
+/*
+ RequireJS 2.1.11 Copyright (c) 2010-2014, The Dojo Foundation All Rights Reserved.
+ Available via the MIT or new BSD license.
+ see: http://github.com/jrburke/requirejs for details
+*/
+var requirejs,require,define;
+(function(ca){function G(b){return"[object Function]"===M.call(b)}function H(b){return"[object Array]"===M.call(b)}function v(b,c){if(b){var d;for(d=0;d<b.length&&(!b[d]||!c(b[d],d,b));d+=1);}}function U(b,c){if(b){var d;for(d=b.length-1;-1<d&&(!b[d]||!c(b[d],d,b));d-=1);}}function s(b,c){return ga.call(b,c)}function j(b,c){return s(b,c)&&b[c]}function B(b,c){for(var d in b)if(s(b,d)&&c(b[d],d))break}function V(b,c,d,g){c&&B(c,function(c,h){if(d||!s(b,h))g&&"object"===typeof c&&c&&!H(c)&&!G(c)&&!(c instanceof
+RegExp)?(b[h]||(b[h]={}),V(b[h],c,d,g)):b[h]=c});return b}function t(b,c){return function(){return c.apply(b,arguments)}}function da(b){throw b;}function ea(b){if(!b)return b;var c=ca;v(b.split("."),function(b){c=c[b]});return c}function C(b,c,d,g){c=Error(c+"\nhttp://requirejs.org/docs/errors.html#"+b);c.requireType=b;c.requireModules=g;d&&(c.originalError=d);return c}function ha(b){function c(a,e,b){var f,n,c,d,g,h,i,I=e&&e.split("/");n=I;var m=l.map,k=m&&m["*"];if(a&&"."===a.charAt(0))if(e){n=
+I.slice(0,I.length-1);a=a.split("/");e=a.length-1;l.nodeIdCompat&&R.test(a[e])&&(a[e]=a[e].replace(R,""));n=a=n.concat(a);d=n.length;for(e=0;e<d;e++)if(c=n[e],"."===c)n.splice(e,1),e-=1;else if(".."===c)if(1===e&&(".."===n[2]||".."===n[0]))break;else 0<e&&(n.splice(e-1,2),e-=2);a=a.join("/")}else 0===a.indexOf("./")&&(a=a.substring(2));if(b&&m&&(I||k)){n=a.split("/");e=n.length;a:for(;0<e;e-=1){d=n.slice(0,e).join("/");if(I)for(c=I.length;0<c;c-=1)if(b=j(m,I.slice(0,c).join("/")))if(b=j(b,d)){f=b;
+g=e;break a}!h&&(k&&j(k,d))&&(h=j(k,d),i=e)}!f&&h&&(f=h,g=i);f&&(n.splice(0,g,f),a=n.join("/"))}return(f=j(l.pkgs,a))?f:a}function d(a){z&&v(document.getElementsByTagName("script"),function(e){if(e.getAttribute("data-requiremodule")===a&&e.getAttribute("data-requirecontext")===i.contextName)return e.parentNode.removeChild(e),!0})}function g(a){var e=j(l.paths,a);if(e&&H(e)&&1<e.length)return e.shift(),i.require.undef(a),i.require([a]),!0}function u(a){var e,b=a?a.indexOf("!"):-1;-1<b&&(e=a.substring(0,
+b),a=a.substring(b+1,a.length));return[e,a]}function m(a,e,b,f){var n,d,g=null,h=e?e.name:null,l=a,m=!0,k="";a||(m=!1,a="_@r"+(M+=1));a=u(a);g=a[0];a=a[1];g&&(g=c(g,h,f),d=j(p,g));a&&(g?k=d&&d.normalize?d.normalize(a,function(a){return c(a,h,f)}):c(a,h,f):(k=c(a,h,f),a=u(k),g=a[0],k=a[1],b=!0,n=i.nameToUrl(k)));b=g&&!d&&!b?"_unnormalized"+(Q+=1):"";return{prefix:g,name:k,parentMap:e,unnormalized:!!b,url:n,originalName:l,isDefine:m,id:(g?g+"!"+k:k)+b}}function q(a){var e=a.id,b=j(k,e);b||(b=k[e]=new i.Module(a));
+return b}function r(a,e,b){var f=a.id,n=j(k,f);if(s(p,f)&&(!n||n.defineEmitComplete))"defined"===e&&b(p[f]);else if(n=q(a),n.error&&"error"===e)b(n.error);else n.on(e,b)}function w(a,e){var b=a.requireModules,f=!1;if(e)e(a);else if(v(b,function(e){if(e=j(k,e))e.error=a,e.events.error&&(f=!0,e.emit("error",a))}),!f)h.onError(a)}function x(){S.length&&(ia.apply(A,[A.length,0].concat(S)),S=[])}function y(a){delete k[a];delete W[a]}function F(a,e,b){var f=a.map.id;a.error?a.emit("error",a.error):(e[f]=
+!0,v(a.depMaps,function(f,c){var d=f.id,g=j(k,d);g&&(!a.depMatched[c]&&!b[d])&&(j(e,d)?(a.defineDep(c,p[d]),a.check()):F(g,e,b))}),b[f]=!0)}function D(){var a,e,b=(a=1E3*l.waitSeconds)&&i.startTime+a<(new Date).getTime(),f=[],c=[],h=!1,k=!0;if(!X){X=!0;B(W,function(a){var i=a.map,m=i.id;if(a.enabled&&(i.isDefine||c.push(a),!a.error))if(!a.inited&&b)g(m)?h=e=!0:(f.push(m),d(m));else if(!a.inited&&(a.fetched&&i.isDefine)&&(h=!0,!i.prefix))return k=!1});if(b&&f.length)return a=C("timeout","Load timeout for modules: "+
+f,null,f),a.contextName=i.contextName,w(a);k&&v(c,function(a){F(a,{},{})});if((!b||e)&&h)if((z||fa)&&!Y)Y=setTimeout(function(){Y=0;D()},50);X=!1}}function E(a){s(p,a[0])||q(m(a[0],null,!0)).init(a[1],a[2])}function K(a){var a=a.currentTarget||a.srcElement,e=i.onScriptLoad;a.detachEvent&&!Z?a.detachEvent("onreadystatechange",e):a.removeEventListener("load",e,!1);e=i.onScriptError;(!a.detachEvent||Z)&&a.removeEventListener("error",e,!1);return{node:a,id:a&&a.getAttribute("data-requiremodule")}}function L(){var a;
+for(x();A.length;){a=A.shift();if(null===a[0])return w(C("mismatch","Mismatched anonymous define() module: "+a[a.length-1]));E(a)}}var X,$,i,N,Y,l={waitSeconds:7,baseUrl:"./",paths:{},bundles:{},pkgs:{},shim:{},config:{}},k={},W={},aa={},A=[],p={},T={},ba={},M=1,Q=1;N={require:function(a){return a.require?a.require:a.require=i.makeRequire(a.map)},exports:function(a){a.usingExports=!0;if(a.map.isDefine)return a.exports?p[a.map.id]=a.exports:a.exports=p[a.map.id]={}},module:function(a){return a.module?
+a.module:a.module={id:a.map.id,uri:a.map.url,config:function(){return j(l.config,a.map.id)||{}},exports:a.exports||(a.exports={})}}};$=function(a){this.events=j(aa,a.id)||{};this.map=a;this.shim=j(l.shim,a.id);this.depExports=[];this.depMaps=[];this.depMatched=[];this.pluginMaps={};this.depCount=0};$.prototype={init:function(a,e,b,f){f=f||{};if(!this.inited){this.factory=e;if(b)this.on("error",b);else this.events.error&&(b=t(this,function(a){this.emit("error",a)}));this.depMaps=a&&a.slice(0);this.errback=
+b;this.inited=!0;this.ignore=f.ignore;f.enabled||this.enabled?this.enable():this.check()}},defineDep:function(a,e){this.depMatched[a]||(this.depMatched[a]=!0,this.depCount-=1,this.depExports[a]=e)},fetch:function(){if(!this.fetched){this.fetched=!0;i.startTime=(new Date).getTime();var a=this.map;if(this.shim)i.makeRequire(this.map,{enableBuildCallback:!0})(this.shim.deps||[],t(this,function(){return a.prefix?this.callPlugin():this.load()}));else return a.prefix?this.callPlugin():this.load()}},load:function(){var a=
+this.map.url;T[a]||(T[a]=!0,i.load(this.map.id,a))},check:function(){if(this.enabled&&!this.enabling){var a,e,b=this.map.id;e=this.depExports;var f=this.exports,c=this.factory;if(this.inited)if(this.error)this.emit("error",this.error);else{if(!this.defining){this.defining=!0;if(1>this.depCount&&!this.defined){if(G(c)){if(this.events.error&&this.map.isDefine||h.onError!==da)try{f=i.execCb(b,c,e,f)}catch(d){a=d}else f=i.execCb(b,c,e,f);this.map.isDefine&&void 0===f&&((e=this.module)?f=e.exports:this.usingExports&&
+(f=this.exports));if(a)return a.requireMap=this.map,a.requireModules=this.map.isDefine?[this.map.id]:null,a.requireType=this.map.isDefine?"define":"require",w(this.error=a)}else f=c;this.exports=f;if(this.map.isDefine&&!this.ignore&&(p[b]=f,h.onResourceLoad))h.onResourceLoad(i,this.map,this.depMaps);y(b);this.defined=!0}this.defining=!1;this.defined&&!this.defineEmitted&&(this.defineEmitted=!0,this.emit("defined",this.exports),this.defineEmitComplete=!0)}}else this.fetch()}},callPlugin:function(){var a=
+this.map,b=a.id,d=m(a.prefix);this.depMaps.push(d);r(d,"defined",t(this,function(f){var d,g;g=j(ba,this.map.id);var J=this.map.name,u=this.map.parentMap?this.map.parentMap.name:null,p=i.makeRequire(a.parentMap,{enableBuildCallback:!0});if(this.map.unnormalized){if(f.normalize&&(J=f.normalize(J,function(a){return c(a,u,!0)})||""),f=m(a.prefix+"!"+J,this.map.parentMap),r(f,"defined",t(this,function(a){this.init([],function(){return a},null,{enabled:!0,ignore:!0})})),g=j(k,f.id)){this.depMaps.push(f);
+if(this.events.error)g.on("error",t(this,function(a){this.emit("error",a)}));g.enable()}}else g?(this.map.url=i.nameToUrl(g),this.load()):(d=t(this,function(a){this.init([],function(){return a},null,{enabled:!0})}),d.error=t(this,function(a){this.inited=!0;this.error=a;a.requireModules=[b];B(k,function(a){0===a.map.id.indexOf(b+"_unnormalized")&&y(a.map.id)});w(a)}),d.fromText=t(this,function(f,c){var g=a.name,J=m(g),k=O;c&&(f=c);k&&(O=!1);q(J);s(l.config,b)&&(l.config[g]=l.config[b]);try{h.exec(f)}catch(j){return w(C("fromtexteval",
+"fromText eval for "+b+" failed: "+j,j,[b]))}k&&(O=!0);this.depMaps.push(J);i.completeLoad(g);p([g],d)}),f.load(a.name,p,d,l))}));i.enable(d,this);this.pluginMaps[d.id]=d},enable:function(){W[this.map.id]=this;this.enabling=this.enabled=!0;v(this.depMaps,t(this,function(a,b){var c,f;if("string"===typeof a){a=m(a,this.map.isDefine?this.map:this.map.parentMap,!1,!this.skipMap);this.depMaps[b]=a;if(c=j(N,a.id)){this.depExports[b]=c(this);return}this.depCount+=1;r(a,"defined",t(this,function(a){this.defineDep(b,
+a);this.check()}));this.errback&&r(a,"error",t(this,this.errback))}c=a.id;f=k[c];!s(N,c)&&(f&&!f.enabled)&&i.enable(a,this)}));B(this.pluginMaps,t(this,function(a){var b=j(k,a.id);b&&!b.enabled&&i.enable(a,this)}));this.enabling=!1;this.check()},on:function(a,b){var c=this.events[a];c||(c=this.events[a]=[]);c.push(b)},emit:function(a,b){v(this.events[a],function(a){a(b)});"error"===a&&delete this.events[a]}};i={config:l,contextName:b,registry:k,defined:p,urlFetched:T,defQueue:A,Module:$,makeModuleMap:m,
+nextTick:h.nextTick,onError:w,configure:function(a){a.baseUrl&&"/"!==a.baseUrl.charAt(a.baseUrl.length-1)&&(a.baseUrl+="/");var b=l.shim,c={paths:!0,bundles:!0,config:!0,map:!0};B(a,function(a,b){c[b]?(l[b]||(l[b]={}),V(l[b],a,!0,!0)):l[b]=a});a.bundles&&B(a.bundles,function(a,b){v(a,function(a){a!==b&&(ba[a]=b)})});a.shim&&(B(a.shim,function(a,c){H(a)&&(a={deps:a});if((a.exports||a.init)&&!a.exportsFn)a.exportsFn=i.makeShimExports(a);b[c]=a}),l.shim=b);a.packages&&v(a.packages,function(a){var b,
+a="string"===typeof a?{name:a}:a;b=a.name;a.location&&(l.paths[b]=a.location);l.pkgs[b]=a.name+"/"+(a.main||"main").replace(ja,"").replace(R,"")});B(k,function(a,b){!a.inited&&!a.map.unnormalized&&(a.map=m(b))});if(a.deps||a.callback)i.require(a.deps||[],a.callback)},makeShimExports:function(a){return function(){var b;a.init&&(b=a.init.apply(ca,arguments));return b||a.exports&&ea(a.exports)}},makeRequire:function(a,e){function g(f,c,d){var j,l;e.enableBuildCallback&&(c&&G(c))&&(c.__requireJsBuild=
+!0);if("string"===typeof f){if(G(c))return w(C("requireargs","Invalid require call"),d);if(a&&s(N,f))return N[f](k[a.id]);if(h.get)return h.get(i,f,a,g);j=m(f,a,!1,!0);j=j.id;return!s(p,j)?w(C("notloaded",'Module name "'+j+'" has not been loaded yet for context: '+b+(a?"":". Use require([])"))):p[j]}L();i.nextTick(function(){L();l=q(m(null,a));l.skipMap=e.skipMap;l.init(f,c,d,{enabled:!0});D()});return g}e=e||{};V(g,{isBrowser:z,toUrl:function(b){var e,d=b.lastIndexOf("."),g=b.split("/")[0];if(-1!==
+d&&(!("."===g||".."===g)||1<d))e=b.substring(d,b.length),b=b.substring(0,d);return i.nameToUrl(c(b,a&&a.id,!0),e,!0)},defined:function(b){return s(p,m(b,a,!1,!0).id)},specified:function(b){b=m(b,a,!1,!0).id;return s(p,b)||s(k,b)}});a||(g.undef=function(b){x();var c=m(b,a,!0),e=j(k,b);d(b);delete p[b];delete T[c.url];delete aa[b];U(A,function(a,c){a[0]===b&&A.splice(c,1)});e&&(e.events.defined&&(aa[b]=e.events),y(b))});return g},enable:function(a){j(k,a.id)&&q(a).enable()},completeLoad:function(a){var b,
+c,f=j(l.shim,a)||{},d=f.exports;for(x();A.length;){c=A.shift();if(null===c[0]){c[0]=a;if(b)break;b=!0}else c[0]===a&&(b=!0);E(c)}c=j(k,a);if(!b&&!s(p,a)&&c&&!c.inited){if(l.enforceDefine&&(!d||!ea(d)))return g(a)?void 0:w(C("nodefine","No define call for "+a,null,[a]));E([a,f.deps||[],f.exportsFn])}D()},nameToUrl:function(a,b,c){var f,d,g;(f=j(l.pkgs,a))&&(a=f);if(f=j(ba,a))return i.nameToUrl(f,b,c);if(h.jsExtRegExp.test(a))f=a+(b||"");else{f=l.paths;a=a.split("/");for(d=a.length;0<d;d-=1)if(g=a.slice(0,
+d).join("/"),g=j(f,g)){H(g)&&(g=g[0]);a.splice(0,d,g);break}f=a.join("/");f+=b||(/^data\:|\?/.test(f)||c?"":".js");f=("/"===f.charAt(0)||f.match(/^[\w\+\.\-]+:/)?"":l.baseUrl)+f}return l.urlArgs?f+((-1===f.indexOf("?")?"?":"&")+l.urlArgs):f},load:function(a,b){h.load(i,a,b)},execCb:function(a,b,c,d){return b.apply(d,c)},onScriptLoad:function(a){if("load"===a.type||ka.test((a.currentTarget||a.srcElement).readyState))P=null,a=K(a),i.completeLoad(a.id)},onScriptError:function(a){var b=K(a);if(!g(b.id))return w(C("scripterror",
+"Script error for: "+b.id,a,[b.id]))}};i.require=i.makeRequire();return i}var h,x,y,D,K,E,P,L,q,Q,la=/(\/\*([\s\S]*?)\*\/|([^:]|^)\/\/(.*)$)/mg,ma=/[^.]\s*require\s*\(\s*["']([^'"\s]+)["']\s*\)/g,R=/\.js$/,ja=/^\.\//;x=Object.prototype;var M=x.toString,ga=x.hasOwnProperty,ia=Array.prototype.splice,z=!!("undefined"!==typeof window&&"undefined"!==typeof navigator&&window.document),fa=!z&&"undefined"!==typeof importScripts,ka=z&&"PLAYSTATION 3"===navigator.platform?/^complete$/:/^(complete|loaded)$/,
+Z="undefined"!==typeof opera&&"[object Opera]"===opera.toString(),F={},r={},S=[],O=!1;if("undefined"===typeof define){if("undefined"!==typeof requirejs){if(G(requirejs))return;r=requirejs;requirejs=void 0}"undefined"!==typeof require&&!G(require)&&(r=require,require=void 0);h=requirejs=function(b,c,d,g){var u,m="_";!H(b)&&"string"!==typeof b&&(u=b,H(c)?(b=c,c=d,d=g):b=[]);u&&u.context&&(m=u.context);(g=j(F,m))||(g=F[m]=h.s.newContext(m));u&&g.configure(u);return g.require(b,c,d)};h.config=function(b){return h(b)};
+h.nextTick="undefined"!==typeof setTimeout?function(b){setTimeout(b,4)}:function(b){b()};require||(require=h);h.version="2.1.11";h.jsExtRegExp=/^\/|:|\?|\.js$/;h.isBrowser=z;x=h.s={contexts:F,newContext:ha};h({});v(["toUrl","undef","defined","specified"],function(b){h[b]=function(){var c=F._;return c.require[b].apply(c,arguments)}});if(z&&(y=x.head=document.getElementsByTagName("head")[0],D=document.getElementsByTagName("base")[0]))y=x.head=D.parentNode;h.onError=da;h.createNode=function(b){var c=
+b.xhtml?document.createElementNS("http://www.w3.org/1999/xhtml","html:script"):document.createElement("script");c.type=b.scriptType||"text/javascript";c.charset="utf-8";c.async=!0;return c};h.load=function(b,c,d){var g=b&&b.config||{};if(z)return g=h.createNode(g,c,d),g.setAttribute("data-requirecontext",b.contextName),g.setAttribute("data-requiremodule",c),g.attachEvent&&!(g.attachEvent.toString&&0>g.attachEvent.toString().indexOf("[native code"))&&!Z?(O=!0,g.attachEvent("onreadystatechange",b.onScriptLoad)):
+(g.addEventListener("load",b.onScriptLoad,!1),g.addEventListener("error",b.onScriptError,!1)),g.src=d,L=g,D?y.insertBefore(g,D):y.appendChild(g),L=null,g;if(fa)try{importScripts(d),b.completeLoad(c)}catch(j){b.onError(C("importscripts","importScripts failed for "+c+" at "+d,j,[c]))}};z&&!r.skipDataMain&&U(document.getElementsByTagName("script"),function(b){y||(y=b.parentNode);if(K=b.getAttribute("data-main"))return q=K,r.baseUrl||(E=q.split("/"),q=E.pop(),Q=E.length?E.join("/")+"/":"./",r.baseUrl=
+Q),q=q.replace(R,""),h.jsExtRegExp.test(q)&&(q=K),r.deps=r.deps?r.deps.concat(q):[q],!0});define=function(b,c,d){var g,h;"string"!==typeof b&&(d=c,c=b,b=null);H(c)||(d=c,c=null);!c&&G(d)&&(c=[],d.length&&(d.toString().replace(la,"").replace(ma,function(b,d){c.push(d)}),c=(1===d.length?["require"]:["require","exports","module"]).concat(c)));if(O){if(!(g=L))P&&"interactive"===P.readyState||U(document.getElementsByTagName("script"),function(b){if("interactive"===b.readyState)return P=b}),g=P;g&&(b||
+(b=g.getAttribute("data-requiremodule")),h=F[g.getAttribute("data-requirecontext")])}(h?h.defQueue:S).push([b,c,d])};define.amd={jQuery:!0};h.exec=function(b){return eval(b)};h(r)}})(this);

+ 10 - 10
examples/js/loaders/AWDLoader.js

@@ -735,8 +735,8 @@ THREE.AWDLoader = (function (){
         // ------------------
         if ( str_type === 1 ) {
 
-          attrib = new THREE.Float32Attribute( str_len/12, 3 );
-          buffer = attrib.array;
+          buffer = new Float32Array( ( str_len / 12 ) * 3 );
+          attrib = new THREE.BufferAttribute( buffer, 3 );
 
           geom.addAttribute( 'position', attrib );
           idx = 0;
@@ -754,7 +754,8 @@ THREE.AWDLoader = (function (){
         // -----------------
         else if (str_type === 2) {
 
-          attrib = new THREE.Uint16Attribute( str_len/2, 1 );
+          buffer = new Uint16Array( str_len / 2 );
+          attrib = new THREE.BufferAttribute( buffer, 1 );
           geom.addAttribute( 'index', attrib );
 
           geom.offsets.push({
@@ -763,7 +764,6 @@ THREE.AWDLoader = (function (){
             count: str_len/2
           });
 
-          buffer = attrib.array;
           idx = 0;
 
           while (this._ptr < str_end) {
@@ -778,8 +778,8 @@ THREE.AWDLoader = (function (){
         // -------------------
         else if (str_type === 3) {
 
-          attrib = new THREE.Float32Attribute( str_len/8, 2 );
-          buffer = attrib.array;
+          buffer = new Float32Array( ( str_len / 8 ) * 2 );
+          attrib = new THREE.BufferAttribute( buffer, 2 );
 
           geom.addAttribute( 'uv', attrib );
           idx = 0;
@@ -794,10 +794,10 @@ THREE.AWDLoader = (function (){
         // NORMALS
         else if (str_type === 4) {
 
-          attrib = new THREE.Float32Attribute( str_len/12, 3 );
+          buffer = new Float32Array( ( str_len / 12 ) * 3 );
+          attrib = new THREE.BufferAttribute( buffer, 3 );
           geom.addAttribute( 'normal', attrib );
-          buffer = attrib.array
-          idx = 0
+          idx = 0;
 
           while (this._ptr < str_end) {
             buffer[idx]   = -this.readF32();
@@ -1222,4 +1222,4 @@ THREE.AWDLoader = (function (){
 
   return AWDLoader;
 
-})();
+})();

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

@@ -85,6 +85,7 @@ THREE.AssimpJSONLoader.prototype = {
 		}
 
 		// read texture coordinates - three.js attaches them to its faces
+		json.texturecoords = json.texturecoords || [];
 		for(i = 0, e = json.texturecoords.length; i < e; ++i) {
 
 			function convertTextureCoords(in_uv, out_faces, out_vertex_uvs) {
@@ -219,7 +220,9 @@ THREE.AssimpJSONLoader.prototype = {
 						has_textures.push(keyname);
 
 						loader.setCrossOrigin(this.crossOrigin);
-						loader.load(scope.texturePath + '/' + prop.value, function(tex) {
+						var material_url = scope.texturePath + '/' + prop.value
+						material_url = material_url.replace(/\\/g, '/');
+						loader.load(material_url, function(tex) {
 							if(tex) {
 								// TODO: read texture settings from assimp.
 								// Wrapping is the default, though.

+ 94 - 119
examples/js/loaders/ColladaLoader.js

@@ -1,7 +1,7 @@
 /**
- * @author Tim Knip / http://www.floorplanner.com/ / tim at floorplanner.com
- * @author Tony Parisi / http://www.tonyparisi.com/ 
- */
+* @author Tim Knip / http://www.floorplanner.com/ / tim at floorplanner.com
+* @author Tony Parisi / http://www.tonyparisi.com/
+*/
 
 THREE.ColladaLoader = function () {
 
@@ -141,7 +141,7 @@ THREE.ColladaLoader = function () {
 		controllers = parseLib( "library_controllers controller", Controller, "controller" );
 		animations = parseLib( "library_animations animation", Animation, "animation" );
 		visualScenes = parseLib( "library_visual_scenes visual_scene", VisualScene, "visual_scene" );
-		
+
 		morphs = [];
 		skins = [];
 
@@ -260,9 +260,9 @@ THREE.ColladaLoader = function () {
 	}
 
 	function parseScene() {
-	
+
 		var sceneElement = COLLADA.querySelectorAll('scene instance_visual_scene')[0];
-		
+
 		if ( sceneElement ) {
 
 			var url = sceneElement.getAttribute( 'url' ).replace( /^#/, '' );
@@ -351,11 +351,11 @@ THREE.ColladaLoader = function () {
 		for ( var id in animations ) {
 
 			var animation = animations[ id ];
-			ID = ID || animation.id; 
+			ID = ID || animation.id;
 			for ( var i = 0; i < animation.sampler.length; i ++ ) {
 
 				var sampler = animation.sampler[ i ];
-				
+
 				sampler.create();
 
 				start = Math.min( start, sampler.startTime );
@@ -502,9 +502,9 @@ THREE.ColladaLoader = function () {
 	function setupSkinningMatrices ( bones, skin ) {
 
 		// FIXME: this is dumb...
-		
+
 		for ( var i = 0; i < bones.length; i ++ ) {
-		
+
 			var bone = bones[ i ];
 			var found = -1;
 
@@ -534,7 +534,7 @@ THREE.ColladaLoader = function () {
 				bone.weights = [];
 
 				for ( var j = 0; j < skin.weights.length; j ++ ) {
-					
+
 					for (var k = 0; k < skin.weights[ j ].length; k ++ ) {
 
 						var w = skin.weights[ j ][ k ];
@@ -573,9 +573,9 @@ THREE.ColladaLoader = function () {
 			bone.matrix = node.matrix;
 			var data = [new THREE.Vector3(),new THREE.Quaternion(),new THREE.Vector3()];
 			bone.matrix.decompose(data[0],data[1],data[2]);
-							
+
 			bone.pos = [data[0].x,data[0].y,data[0].z];
-							
+
 			bone.scl = [data[2].x,data[2].y,data[2].z];
 			bone.rotq = [data[1].x,data[1].y,data[1].z,data[1].w];
 			list.push(bone);
@@ -620,7 +620,7 @@ THREE.ColladaLoader = function () {
 
 				o = geometry.vertices[vidx];
 				s = skinned[vidx];
-				
+
 				v.x = o.x;
 				v.y = o.y;
 				v.z = o.z;
@@ -644,9 +644,6 @@ THREE.ColladaLoader = function () {
 
 	function applySkin ( geometry, instanceCtrl, frame ) {
 
-		// TODO: get this from the renderer or options
-		var maxbones = 30;
-		
 		var skinController = controllers[ instanceCtrl.url ];
 
 		frame = frame !== undefined ? frame : 40;
@@ -668,7 +665,7 @@ THREE.ColladaLoader = function () {
 		var animationBounds = calcAnimationBounds();
 		var skeleton = daeScene.getChildById( instanceCtrl.skeleton[0], true ) ||
 					   daeScene.getChildBySid( instanceCtrl.skeleton[0], true );
-		
+
 		//flatten the skeleton into a list of bones
 		var bonelist = flattenSkeleton(skeleton);
 		var joints = skinController.skin.joints;
@@ -723,7 +720,7 @@ THREE.ColladaLoader = function () {
 
 			var indicies = new THREE.Vector4(weights[i][0]?weights[i][0].joint:0,weights[i][1]?weights[i][1].joint:0,weights[i][2]?weights[i][2].joint:0,weights[i][3]?weights[i][3].joint:0);
 			var weight = new THREE.Vector4(weights[i][0]?weights[i][0].weight:0,weights[i][1]?weights[i][1].weight:0,weights[i][2]?weights[i][2].weight:0,weights[i][3]?weights[i][3].weight:0);
-			
+
 			skinIndices.push(indicies);
 			skinWeights.push(weight);
 
@@ -733,7 +730,7 @@ THREE.ColladaLoader = function () {
 		geometry.skinWeights = skinWeights;
 		geometry.bones = sortedbones;
 		// process animation, or simply pose the rig if no animation
-		
+
 		//create an animation for the animated bones
 		//NOTE: this has no effect when using morphtargets
 		var animationdata = {"name":animationBounds.ID,"fps":30,"length":animationBounds.frames/30,"hierarchy":[]};
@@ -744,12 +741,12 @@ THREE.ColladaLoader = function () {
 
 		}
 
-		//if using hardware skinning, move the vertices into the binding pose
-		if(sortedbones.length < maxbones) {
+		console.log( 'ColladaLoader:', animationBounds.ID + ' has ' + sortedbones.length + ' bones.' );
 
-			skinToBindPose(geometry,skeleton,skinController);
 
-		}
+
+		skinToBindPose(geometry,skeleton,skinController);
+
 
 		for ( frame = 0; frame < animationBounds.frames; frame ++ ) {
 
@@ -761,81 +758,37 @@ THREE.ColladaLoader = function () {
 			setupSkeleton( skeleton, bones, frame );
 			setupSkinningMatrices( bones, skinController.skin );
 
-			//if using hardware skinning, just hook up the animiation data
-			if(sortedbones.length < maxbones) {
+			for(var i = 0; i < bones.length; i ++) {
 
-				for(var i = 0; i < bones.length; i ++) {
+				for(var j = 0; j < animationdata.hierarchy.length; j ++) {
 
-					for(var j = 0; j < animationdata.hierarchy.length; j ++) {
+					if(animationdata.hierarchy[j].name == bones[i].sid) {
 
-						if(animationdata.hierarchy[j].name == bones[i].sid) {
+						var key = {};
+						key.time = (frame/30);
+						key.matrix = bones[i].animatrix;
 
-							var key = {};
-							key.time = (frame/30);
-							key.matrix = bones[i].animatrix;
-							
-							if(frame == 0)
-								bones[i].matrix = key.matrix;
-							
-							var data = [new THREE.Vector3(),new THREE.Quaternion(),new THREE.Vector3()];
-							key.matrix.decompose(data[0],data[1],data[2]);
-							
-							key.pos = [data[0].x,data[0].y,data[0].z];
-							
-							key.scl = [data[2].x,data[2].y,data[2].z];
-							key.rot = data[1];
+						if(frame == 0)
+							bones[i].matrix = key.matrix;
 
-							animationdata.hierarchy[j].keys.push(key);
+						var data = [new THREE.Vector3(),new THREE.Quaternion(),new THREE.Vector3()];
+						key.matrix.decompose(data[0],data[1],data[2]);
 
-						}
+						key.pos = [data[0].x,data[0].y,data[0].z];
 
-					}
+						key.scl = [data[2].x,data[2].y,data[2].z];
+						key.rot = data[1];
 
-				}
-
-				geometry.animation = animationdata;
-
-			} else {
-
-				// otherwise, process the animation into morphtargets
-				
-				for ( i = 0; i < geometry.vertices.length; i++ ) {
-
-					skinned.push( new THREE.Vector3() );
-
-				}
-
-				for ( i = 0; i < bones.length; i ++ ) {
-
-					if ( bones[ i ].type != 'JOINT' ) continue;
-
-					for ( j = 0; j < bones[ i ].weights.length; j ++ ) {
-
-						w = bones[ i ].weights[ j ];
-						vidx = w.index;
-						weight = w.weight;
-
-						o = geometry.vertices[vidx];
-						s = skinned[vidx];
-
-						v.x = o.x;
-						v.y = o.y;
-						v.z = o.z;
-
-						v.applyMatrix4( bones[i].skinningMatrix );
-
-						s.x += (v.x * weight);
-						s.y += (v.y * weight);
-						s.z += (v.z * weight);
+						animationdata.hierarchy[j].keys.push(key);
 
 					}
 
 				}
 
-			geometry.morphTargets.push( { name: "target_" + frame, vertices: skinned } );
-
 			}
 
+			geometry.animation = animationdata;
+
 		}
 
 	};
@@ -997,7 +950,7 @@ THREE.ColladaLoader = function () {
 
 				if ( skinController !== undefined ) {
 
-					
+
 					applySkin( geom, skinController );
 
 					if(geom.morphTargets.length > 0) {
@@ -1010,8 +963,8 @@ THREE.ColladaLoader = function () {
 						material.morphTargets = false;
 						material.skinning = true;
 
-					}				
-					
+					}
+
 
 					mesh = new THREE.SkinnedMesh( geom, material, false );
 
@@ -1021,8 +974,8 @@ THREE.ColladaLoader = function () {
 					//mesh.skinInstanceController = skinController;
 					mesh.name = 'skin_' + skins.length;
 
-					
-					
+
+
 					//mesh.animationHandle.setKey(0);
 					skins.push( mesh );
 
@@ -1060,7 +1013,7 @@ THREE.ColladaLoader = function () {
 			var instance_camera = node.cameras[i];
 			var cparams = cameras[instance_camera.url];
 
-			var cam = new THREE.PerspectiveCamera(cparams.yfov, parseFloat(cparams.aspect_ratio), 
+			var cam = new THREE.PerspectiveCamera(cparams.yfov, parseFloat(cparams.aspect_ratio),
 					parseFloat(cparams.znear), parseFloat(cparams.zfar));
 
 			obj.add(cam);
@@ -1076,7 +1029,7 @@ THREE.ColladaLoader = function () {
 
 				var color = lparams.color.getHex();
 				var intensity = lparams.intensity;
-				var distance = 0;
+				var distance = lparams.distance;
 				var angle = lparams.falloff_angle;
 				var exponent; // Intentionally undefined, don't know what this is yet
 
@@ -1121,7 +1074,7 @@ THREE.ColladaLoader = function () {
 
 		if ( options.centerGeometry && obj.geometry ) {
 
-			var delta = THREE.GeometryUtils.center( obj.geometry );
+			var delta = obj.geometry.center();
 			delta.multiply( obj.scale );
 			delta.applyQuaternion( obj.quaternion );
 
@@ -2442,14 +2395,14 @@ THREE.ColladaLoader = function () {
 					break;
 
 				case 'bind_material':
-			
+
 					var instances = child.querySelectorAll('instance_material');
-					
+
 					for ( var j = 0; j < instances.length; j ++ ){
 
 						var instance = instances[j];
 						this.instance_material.push( (new InstanceMaterial()).parse(instance) );
-						
+
 					}
 
 
@@ -2503,7 +2456,7 @@ THREE.ColladaLoader = function () {
 			if ( child.nodeName == 'bind_material' ) {
 
 				var instances = child.querySelectorAll('instance_material');
-					
+
 				for ( var j = 0; j < instances.length; j ++ ) {
 
 					var instance = instances[j];
@@ -2652,7 +2605,7 @@ THREE.ColladaLoader = function () {
 		var source, numParams;
 		var vcIndex = 0, vcount = 3, maxOffset = 0;
 		var texture_sets = [];
-		
+
 		for ( j = 0; j < inputs.length; j ++ ) {
 
 			input = inputs[ j ];
@@ -2821,7 +2774,7 @@ THREE.ColladaLoader = function () {
 				} else if ( vcount === 4 ) {
 
 					faces.push( new THREE.Face3( vs[0], vs[1], vs[3], [ns[0], ns[1], ns[3]], cs.length ? [cs[0], cs[1], cs[3]] : new THREE.Color() ) );
-					
+
 					faces.push( new THREE.Face3( vs[1], vs[2], vs[3], [ns[1], ns[2], ns[3]], cs.length ? [cs[1], cs[2], cs[3]] : new THREE.Color() ) );
 
 				} else if ( vcount > 4 && options.subdivideFaces ) {
@@ -3242,7 +3195,7 @@ THREE.ColladaLoader = function () {
 			this.opaque = element.getAttribute('opaque');
 
 		}
-		
+
 		for ( var i = 0; i < element.childNodes.length; i ++ ) {
 
 			var child = element.childNodes[ i ];
@@ -3321,17 +3274,17 @@ THREE.ColladaLoader = function () {
 
 				case 'wrapU':
 				case 'wrapV':
-					
+
 					// some dae have a value of true which becomes NaN via parseInt
 
 					if ( child.textContent.toUpperCase() === 'TRUE' ) {
-					
+
 						this.texOpts[ child.nodeName ] = 1;
-					
+
 					} else {
-					
+
 						this.texOpts[ child.nodeName ] = parseInt( child.textContent );
-					
+
 					}
 					break;
 
@@ -3387,7 +3340,7 @@ THREE.ColladaLoader = function () {
 						} else if ( bumpType.toLowerCase() === "normalmap" ) {
 							this[ 'normal' ] = ( new ColorOrTexture() ).parse( child );
 						} else {
-							console.error( "Shader.prototype.parse: Invalid value for attribute 'bumptype' (" + bumpType + 
+							console.error( "Shader.prototype.parse: Invalid value for attribute 'bumptype' (" + bumpType +
 								           ") - valid bumptypes are 'HEIGHTFIELD' and 'NORMALMAP' - defaulting to 'HEIGHTFIELD'" );
 							this[ 'bump' ] = ( new ColorOrTexture() ).parse( child );
 						}
@@ -3431,11 +3384,8 @@ THREE.ColladaLoader = function () {
 		if (this['transparency'] !== undefined && this['transparent'] !== undefined) {
 			// convert transparent color RBG to average value
 			var transparentColor = this['transparent'];
-			var transparencyLevel = (this.transparent.color.r +
-										this.transparent.color.g + 
-										this.transparent.color.b)
-										/ 3 * this.transparency;
-			
+			var transparencyLevel = (this.transparent.color.r + this.transparent.color.g + this.transparent.color.b) / 3 * this.transparency;
+
 			if (transparencyLevel > 0) {
 				transparent = true;
 				props[ 'transparent' ] = true;
@@ -3444,16 +3394,16 @@ THREE.ColladaLoader = function () {
 			}
 
 		}
-		
+
 		var keys = {
-			'diffuse':'map', 
+			'diffuse':'map',
 			'ambient':'lightMap' ,
 			'specular':'specularMap',
 			'emission':'emissionMap',
 			'bump':'bumpMap',
 			'normal':'normalMap'
 			};
-		
+
 		for ( var prop in this ) {
 
 			switch ( prop ) {
@@ -3481,7 +3431,28 @@ THREE.ColladaLoader = function () {
 
 								if (image) {
 
-									var texture = THREE.ImageUtils.loadTexture(baseUrl + image.init_from);
+									var url = baseUrl + image.init_from;
+
+									var texture;
+									var loader = THREE.Loader.Handlers.get( url );
+
+									if ( loader !== null ) {
+
+										texture = loader.load( url );
+
+									} else {
+
+										texture = new THREE.Texture();
+										loader = new THREE.ImageLoader();
+										loader.load( url, function ( image ) {
+
+											texture.image = image;
+											texture.needsUpdate = true;
+
+										} );
+
+									}
+									
 									texture.wrapS = cot.texOpts.wrapU ? THREE.RepeatWrapping : THREE.ClampToEdgeWrapping;
 									texture.wrapT = cot.texOpts.wrapV ? THREE.RepeatWrapping : THREE.ClampToEdgeWrapping;
 									texture.offset.x = cot.texOpts.offsetU;
@@ -3833,7 +3804,7 @@ THREE.ColladaLoader = function () {
 					this.shader = ( new Shader( child.nodeName, this ) ).parse( child );
 					break;
 				case 'extra':
-					this.parseExtra(child);	
+					this.parseExtra(child);
 					break;
 				default:
 					break;
@@ -3843,7 +3814,7 @@ THREE.ColladaLoader = function () {
 		}
 
 	};
-	
+
 	Effect.prototype.parseExtra = function ( element ) {
 
 		for ( var i = 0; i < element.childNodes.length; i ++ ) {
@@ -3864,7 +3835,7 @@ THREE.ColladaLoader = function () {
 		}
 
 	};
-	
+
 	Effect.prototype.parseExtraTechnique= function ( element ) {
 
 		for ( var i = 0; i < element.childNodes.length; i ++ ) {
@@ -4185,7 +4156,7 @@ THREE.ColladaLoader = function () {
 			if ( member && type == 'translate' ) {
 				data = getConvertedTranslation( member, data );
 			}
-			
+
 		}
 
 		return data;
@@ -4515,6 +4486,10 @@ THREE.ColladaLoader = function () {
 
 								this.falloff_angle = parseFloat( child.textContent );
 								break;
+
+							case 'quadratic_attenuation':
+								var f = parseFloat( child.textContent );
+								this.distance = f ? Math.sqrt( 1/f ) : 0;
 						}
 
 					}
@@ -4726,7 +4701,7 @@ THREE.ColladaLoader = function () {
 		obj.doubleSided = false;
 
 		var node = element.querySelectorAll('extra double_sided')[0];
-	
+
 		if ( node ) {
 
 			if ( node && parseInt( node.textContent, 10 ) === 1 ) {

+ 347 - 0
examples/js/loaders/DDSLoader.js

@@ -0,0 +1,347 @@
+/*
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.DDSLoader = function () {};
+
+THREE.DDSLoader.prototype = {
+
+	constructor: THREE.DDSLoader,
+
+	load: function ( url, onLoad, onError ) {
+
+		var scope = this;
+
+		var images = [];
+
+		var texture = new THREE.CompressedTexture();
+		texture.image = images;
+
+		// no flipping for cube textures
+		// (also flipping doesn't work for compressed textures )
+
+		texture.flipY = false;
+
+		// can't generate mipmaps for compressed textures
+		// mips must be embedded in DDS files
+
+		texture.generateMipmaps = false;
+
+		if ( url instanceof Array ) {
+
+			var loaded = 0;
+
+			var loader = new THREE.XHRLoader();
+			loader.setResponseType( 'arraybuffer' );
+
+			var loadTexture = function ( i ) {
+		
+				loader.load( url[ i ], function ( buffer ) {
+
+					var dds = scope.parse( buffer, true );
+
+					images[ i ] = {
+						width: dds.width,
+						height: dds.height,
+						format: dds.format,
+						mipmaps: dds.mipmaps
+					}
+
+					loaded += 1;
+
+					if ( loaded === 6 ) {
+
+						texture.format = dds.format;
+						texture.needsUpdate = true;
+
+						if ( onLoad ) onLoad( texture );
+
+					}
+
+				} );
+
+			}
+
+			for ( var i = 0, il = url.length; i < il; ++ i ) {
+
+				loadTexture( i );
+
+			}
+
+		} else {
+
+			// compressed cubemap texture stored in a single DDS file
+
+			var loader = new THREE.XHRLoader();
+			loader.setResponseType( 'arraybuffer' );
+			loader.load( url, function ( buffer ) {
+
+				var dds = scope.parse( buffer, true );
+
+				if ( dds.isCubemap ) {
+
+					var faces = dds.mipmaps.length / dds.mipmapCount;
+
+					for ( var f = 0; f < faces; f ++ ) {
+
+						images[ f ] = { mipmaps : [] };
+
+						for ( var i = 0; i < dds.mipmapCount; i ++ ) {
+
+							images[ f ].mipmaps.push( dds.mipmaps[ f * dds.mipmapCount + i ] );
+							images[ f ].format = dds.format;
+							images[ f ].width = dds.width;
+							images[ f ].height = dds.height;
+
+						}
+
+					}
+
+				} else {
+
+					texture.image.width = dds.width;
+					texture.image.height = dds.height;
+					texture.mipmaps = dds.mipmaps;
+
+				}
+
+				texture.format = dds.format;
+				texture.needsUpdate = true;
+
+				if ( onLoad ) onLoad( texture );
+
+			} );
+
+		}
+
+		return texture;
+
+	},
+
+	parse: function ( buffer, loadMipmaps ) {
+
+		var dds = { mipmaps: [], width: 0, height: 0, format: null, mipmapCount: 1 };
+
+		// Adapted from @toji's DDS utils
+		//	https://github.com/toji/webgl-texture-utils/blob/master/texture-util/dds.js
+
+		// All values and structures referenced from:
+		// http://msdn.microsoft.com/en-us/library/bb943991.aspx/
+
+		var DDS_MAGIC = 0x20534444;
+
+		var DDSD_CAPS = 0x1,
+			DDSD_HEIGHT = 0x2,
+			DDSD_WIDTH = 0x4,
+			DDSD_PITCH = 0x8,
+			DDSD_PIXELFORMAT = 0x1000,
+			DDSD_MIPMAPCOUNT = 0x20000,
+			DDSD_LINEARSIZE = 0x80000,
+			DDSD_DEPTH = 0x800000;
+
+		var DDSCAPS_COMPLEX = 0x8,
+			DDSCAPS_MIPMAP = 0x400000,
+			DDSCAPS_TEXTURE = 0x1000;
+
+		var DDSCAPS2_CUBEMAP = 0x200,
+			DDSCAPS2_CUBEMAP_POSITIVEX = 0x400,
+			DDSCAPS2_CUBEMAP_NEGATIVEX = 0x800,
+			DDSCAPS2_CUBEMAP_POSITIVEY = 0x1000,
+			DDSCAPS2_CUBEMAP_NEGATIVEY = 0x2000,
+			DDSCAPS2_CUBEMAP_POSITIVEZ = 0x4000,
+			DDSCAPS2_CUBEMAP_NEGATIVEZ = 0x8000,
+			DDSCAPS2_VOLUME = 0x200000;
+
+		var DDPF_ALPHAPIXELS = 0x1,
+			DDPF_ALPHA = 0x2,
+			DDPF_FOURCC = 0x4,
+			DDPF_RGB = 0x40,
+			DDPF_YUV = 0x200,
+			DDPF_LUMINANCE = 0x20000;
+
+		function fourCCToInt32( value ) {
+
+			return value.charCodeAt(0) +
+				(value.charCodeAt(1) << 8) +
+				(value.charCodeAt(2) << 16) +
+				(value.charCodeAt(3) << 24);
+
+		}
+
+		function int32ToFourCC( value ) {
+
+			return String.fromCharCode(
+				value & 0xff,
+				(value >> 8) & 0xff,
+				(value >> 16) & 0xff,
+				(value >> 24) & 0xff
+			);
+		}
+
+		function loadARGBMip( buffer, dataOffset, width, height ) {
+			var dataLength = width*height*4;
+			var srcBuffer = new Uint8Array( buffer, dataOffset, dataLength );
+			var byteArray = new Uint8Array( dataLength );
+			var dst = 0;
+			var src = 0;
+			for ( var y = 0; y < height; y++ ) {
+				for ( var x = 0; x < width; x++ ) {
+					var b = srcBuffer[src]; src++;
+					var g = srcBuffer[src]; src++;
+					var r = srcBuffer[src]; src++;
+					var a = srcBuffer[src]; src++;
+					byteArray[dst] = r; dst++;	//r
+					byteArray[dst] = g; dst++;	//g
+					byteArray[dst] = b; dst++;	//b
+					byteArray[dst] = a; dst++;	//a
+				}
+			}
+			return byteArray;
+		}
+
+		var FOURCC_DXT1 = fourCCToInt32("DXT1");
+		var FOURCC_DXT3 = fourCCToInt32("DXT3");
+		var FOURCC_DXT5 = fourCCToInt32("DXT5");
+
+		var headerLengthInt = 31; // The header length in 32 bit ints
+
+		// Offsets into the header array
+
+		var off_magic = 0;
+
+		var off_size = 1;
+		var off_flags = 2;
+		var off_height = 3;
+		var off_width = 4;
+
+		var off_mipmapCount = 7;
+
+		var off_pfFlags = 20;
+		var off_pfFourCC = 21;
+		var off_RGBBitCount = 22;
+		var off_RBitMask = 23;
+		var off_GBitMask = 24;
+		var off_BBitMask = 25;
+		var off_ABitMask = 26;
+
+		var off_caps = 27;
+		var off_caps2 = 28;
+		var off_caps3 = 29;
+		var off_caps4 = 30;
+
+		// Parse header
+
+		var header = new Int32Array( buffer, 0, headerLengthInt );
+
+		if ( header[ off_magic ] !== DDS_MAGIC ) {
+
+			console.error( 'THREE.DDSLoader.parse: Invalid magic number in DDS header.' );
+			return dds;
+
+		}
+
+		if ( ! header[ off_pfFlags ] & DDPF_FOURCC ) {
+
+			console.error( 'THREE.DDSLoader.parse: Unsupported format, must contain a FourCC code.' );
+			return dds;
+
+		}
+
+		var blockBytes;
+
+		var fourCC = header[ off_pfFourCC ];
+
+		var isRGBAUncompressed = false;
+
+		switch ( fourCC ) {
+
+			case FOURCC_DXT1:
+
+				blockBytes = 8;
+				dds.format = THREE.RGB_S3TC_DXT1_Format;
+				break;
+
+			case FOURCC_DXT3:
+
+				blockBytes = 16;
+				dds.format = THREE.RGBA_S3TC_DXT3_Format;
+				break;
+
+			case FOURCC_DXT5:
+
+				blockBytes = 16;
+				dds.format = THREE.RGBA_S3TC_DXT5_Format;
+				break;
+
+			default:
+
+				if( header[off_RGBBitCount] ==32 
+					&& header[off_RBitMask]&0xff0000
+					&& header[off_GBitMask]&0xff00 
+					&& header[off_BBitMask]&0xff
+					&& header[off_ABitMask]&0xff000000  ) {
+					isRGBAUncompressed = true;
+					blockBytes = 64;
+					dds.format = THREE.RGBAFormat;
+				} else {
+					console.error( 'THREE.DDSLoader.parse: Unsupported FourCC code ', int32ToFourCC( fourCC ) );
+					return dds;
+				}
+		}
+
+		dds.mipmapCount = 1;
+
+		if ( header[ off_flags ] & DDSD_MIPMAPCOUNT && loadMipmaps !== false ) {
+
+			dds.mipmapCount = Math.max( 1, header[ off_mipmapCount ] );
+
+		}
+
+		//TODO: Verify that all faces of the cubemap are present with DDSCAPS2_CUBEMAP_POSITIVEX, etc.
+
+		dds.isCubemap = header[ off_caps2 ] & DDSCAPS2_CUBEMAP ? true : false;
+
+		dds.width = header[ off_width ];
+		dds.height = header[ off_height ];
+
+		var dataOffset = header[ off_size ] + 4;
+
+		// Extract mipmaps buffers
+
+		var width = dds.width;
+		var height = dds.height;
+
+		var faces = dds.isCubemap ? 6 : 1;
+
+		for ( var face = 0; face < faces; face ++ ) {
+
+			for ( var i = 0; i < dds.mipmapCount; i ++ ) {
+
+				if( isRGBAUncompressed ) {
+					var byteArray = loadARGBMip( buffer, dataOffset, width, height );
+					var dataLength = byteArray.length;
+				} else {
+					var dataLength = Math.max( 4, width ) / 4 * Math.max( 4, height ) / 4 * blockBytes;
+					var byteArray = new Uint8Array( buffer, dataOffset, dataLength );
+				}
+				
+				var mipmap = { "data": byteArray, "width": width, "height": height };
+				dds.mipmaps.push( mipmap );
+
+				dataOffset += dataLength;
+
+				width = Math.max( width * 0.5, 1 );
+				height = Math.max( height * 0.5, 1 );
+
+			}
+
+			width = dds.width;
+			height = dds.height;
+
+		}
+
+		return dds;
+
+	}
+
+};

+ 8 - 6
examples/js/loaders/MTLLoader.js

@@ -363,18 +363,18 @@ THREE.MTLLoader.MaterialCreator.prototype = {
 
 	loadTexture: function ( url, mapping, onLoad, onError ) {
 
-		var isCompressed = /\.dds$/i.test( url );
+		var texture;
+		var loader = THREE.Loader.Handlers.get( url );
 
-		if ( isCompressed ) {
+		if ( loader !== null ) {
 
-			var texture = THREE.ImageUtils.loadCompressedTexture( url, mapping, onLoad, onError );
+			texture = loader.load( url, onLoad );
 
 		} else {
 
-			var image = new Image();
-			var texture = new THREE.Texture( image, mapping );
+			texture = new THREE.Texture();
 
-			var loader = new THREE.ImageLoader();
+			loader = new THREE.ImageLoader();
 			loader.crossOrigin = this.crossOrigin;
 			loader.load( url, function ( image ) {
 
@@ -387,6 +387,8 @@ THREE.MTLLoader.MaterialCreator.prototype = {
 
 		}
 
+		texture.mapping = mapping;
+
 		return texture;
 
 	}

+ 4 - 2
examples/js/loaders/OBJLoader.js

@@ -190,7 +190,9 @@ THREE.OBJLoader.prototype = {
 
 		var face_pattern4 = /f( +(-?\d+)\/\/(-?\d+))( +(-?\d+)\/\/(-?\d+))( +(-?\d+)\/\/(-?\d+))( +(-?\d+)\/\/(-?\d+))?/
 
-		//
+		// fixes
+
+		text = text.replace( /\\\r\n/g, '' ); // handles line continuations \
 
 		var lines = text.split( '\n' );
 
@@ -324,4 +326,4 @@ THREE.OBJLoader.prototype = {
 
 	}
 
-};
+};

+ 0 - 2
examples/js/loaders/OBJMTLLoader.js

@@ -94,7 +94,6 @@ THREE.OBJMTLLoader.prototype = {
 
 				geometry = new THREE.Geometry();
 				mesh = new THREE.Mesh( geometry, material );
-				verticesCount = 0;
 
 			}
 
@@ -119,7 +118,6 @@ THREE.OBJMTLLoader.prototype = {
 		var mesh = new THREE.Mesh( geometry, material );
 
 		var vertices = [];
-		var verticesCount = 0;
 		var normals = [];
 		var uvs = [];
 

+ 16 - 62
examples/js/loaders/PDBLoader.js

@@ -2,75 +2,28 @@
  * @author alteredq / http://alteredqualia.com/
  */
 
-THREE.PDBLoader = function () {};
+THREE.PDBLoader = function ( manager ) {
 
-THREE.PDBLoader.prototype = {
-
-	constructor: THREE.OBJLoader,
-
-	load: function ( url, callback ) {
-
-		var worker, scope = this;
-
-		this.loadAjaxPDB( this, url, callback );
-
-	},
-
-	loadAjaxPDB: function ( context, url, callback, callbackProgress ) {
-
-		var xhr = new XMLHttpRequest();
-
-		var length = 0;
-
-		xhr.onreadystatechange = function () {
-
-			if ( xhr.readyState === xhr.DONE ) {
-
-				if ( xhr.status === 200 || xhr.status === 0 ) {
-
-					if ( xhr.responseText ) {
-
-						var json = context.parsePDB( xhr.responseText );
-						context.createModel( json, callback );
-
-					} else {
+	this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
 
-						console.warn( "THREE.PDBLoader: [" + url + "] seems to be unreachable or file there is empty" );
+};
 
-					}
-
-				} else {
-
-					console.error( "THREE.PDBLoader: Couldn't load [" + url + "] [" + xhr.status + "]" );
-
-				}
-
-			} else if ( xhr.readyState === xhr.LOADING ) {
-
-				if ( callbackProgress ) {
-
-					if ( length === 0 ) {
-
-						length = xhr.getResponseHeader( "Content-Length" );
+THREE.PDBLoader.prototype = {
 
-					}
+	constructor: THREE.PDBLoader,
 
-					callbackProgress( { total: length, loaded: xhr.responseText.length } );
+	load: function ( url, onLoad ) {
 
-				}
+		var scope = this;
 
-			} else if ( xhr.readyState === xhr.HEADERS_RECEIVED ) {
+		var loader = new THREE.XHRLoader( scope.manager );
+		loader.setCrossOrigin( this.crossOrigin );
+		loader.load( url, function ( text ) {
 
-				length = xhr.getResponseHeader( "Content-Length" );
+			var json = scope.parsePDB( text );
+			scope.createModel( json, onLoad );
 
-			}
-
-		};
-
-		xhr.open( "GET", url, true );
-		if ( xhr.overrideMimeType ) xhr.overrideMimeType( "text/plain; charset=x-user-defined" );
-		xhr.setRequestHeader( "Content-Type", "text/plain" );
-		xhr.send( null );
+		} );
 
 	},
 
@@ -114,7 +67,7 @@ THREE.PDBLoader.prototype = {
 					// doesn't really work as almost all PDBs
 					// have just normal bonds appearing multiple
 					// times instead of being double/triple bonds
-					//bonds[bhash[h]][2] += 1;
+					// bonds[bhash[h]][2] += 1;
 
 				}
 
@@ -194,6 +147,7 @@ THREE.PDBLoader.prototype = {
 			var r = atom[ 3 ][ 0 ] / 255;
 			var g = atom[ 3 ][ 1 ] / 255;
 			var b = atom[ 3 ][ 2 ] / 255;
+
 			var color = new THREE.Color();
 			color.setRGB( r, g, b );
 
@@ -218,7 +172,7 @@ THREE.PDBLoader.prototype = {
 
 		}
 
-		callback( geometryAtoms, geometryBonds );
+		callback( geometryAtoms, geometryBonds, json );
 
 	}
 

+ 14 - 8
examples/js/loaders/STLLoader.js

@@ -50,7 +50,7 @@ THREE.STLLoader.prototype.load = function ( url, callback ) {
 
 		} else {
 
-			scope.dispatchEvent( { type: 'error', message: 'Couldn\'t load URL [' + url + ']', response: event.target.responseText } );
+			scope.dispatchEvent( { type: 'error', message: 'Couldn\'t load URL [' + url + ']', response: event.target.statusText } );
 
 		}
 
@@ -108,7 +108,10 @@ THREE.STLLoader.prototype.parseBinary = function ( data ) {
 
 	var offset = 0;
 
-	var geometry = new THREE.TypedGeometry( faces );
+	var geometry = new THREE.BufferGeometry();
+
+	var vertices = new Float32Array( faces * 3 * 3 );
+	var normals = new Float32Array( faces * 3 * 3 );
 
 	for ( var face = 0; face < faces; face ++ ) {
 
@@ -118,13 +121,13 @@ THREE.STLLoader.prototype.parseBinary = function ( data ) {
 
 			var vertexstart = start + i * 12;
 
-			geometry.vertices[ offset     ] = reader.getFloat32( vertexstart, true );
-			geometry.vertices[ offset + 1 ] = reader.getFloat32( vertexstart + 4, true );
-			geometry.vertices[ offset + 2 ] = reader.getFloat32( vertexstart + 8, true );
+			vertices[ offset     ] = reader.getFloat32( vertexstart, true );
+			vertices[ offset + 1 ] = reader.getFloat32( vertexstart + 4, true );
+			vertices[ offset + 2 ] = reader.getFloat32( vertexstart + 8, true );
 
-			geometry.normals[ offset     ] = reader.getFloat32( start    , true );
-			geometry.normals[ offset + 1 ] = reader.getFloat32( start + 4, true );
-			geometry.normals[ offset + 2 ] = reader.getFloat32( start + 8, true );
+			normals[ offset     ] = reader.getFloat32( start    , true );
+			normals[ offset + 1 ] = reader.getFloat32( start + 4, true );
+			normals[ offset + 2 ] = reader.getFloat32( start + 8, true );
 
 			offset += 3;
 
@@ -132,6 +135,9 @@ THREE.STLLoader.prototype.parseBinary = function ( data ) {
 
 	}
 
+	geometry.addAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) );
+	geometry.addAttribute( 'normal', new THREE.BufferAttribute( normals, 3 ) );
+
 	return geometry;
 
 };

+ 31 - 0
examples/js/loaders/SVGLoader.js

@@ -0,0 +1,31 @@
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author zz85 / http://joshuakoo.com/
+ */
+
+THREE.SVGLoader = function ( manager ) {
+
+	this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
+
+};
+
+THREE.SVGLoader.prototype = {
+
+	constructor: THREE.MaterialLoader,
+
+	load: function ( url, onLoad, onProgress, onError ) {
+
+		var parser = new DOMParser();
+
+		var loader = new THREE.XHRLoader();
+		loader.setCrossOrigin( this.crossOrigin );
+		loader.load( url, function ( svgString ) {
+
+			var doc = parser.parseFromString( svgString, 'image/svg+xml' );  // application/xml
+
+			onLoad( doc.firstChild );
+
+		} );
+
+	}
+};

+ 1259 - 0
examples/js/loaders/SceneLoader.js

@@ -0,0 +1,1259 @@
+/**
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.SceneLoader = function () {
+
+	this.onLoadStart = function () {};
+	this.onLoadProgress = function() {};
+	this.onLoadComplete = function () {};
+
+	this.callbackSync = function () {};
+	this.callbackProgress = function () {};
+
+	this.geometryHandlers = {};
+	this.hierarchyHandlers = {};
+
+	this.addGeometryHandler( "ascii", THREE.JSONLoader );
+
+};
+
+THREE.SceneLoader.prototype = {
+
+	constructor: THREE.SceneLoader,
+
+	load: function ( url, onLoad, onProgress, onError ) {
+
+		var scope = this;
+
+		var loader = new THREE.XHRLoader( scope.manager );
+		loader.setCrossOrigin( this.crossOrigin );
+		loader.load( url, function ( text ) {
+
+			scope.parse( JSON.parse( text ), onLoad, url );
+
+		} );
+
+	},
+
+	setCrossOrigin: function ( value ) {
+
+		this.crossOrigin = value;
+
+	},
+
+	addGeometryHandler: function ( typeID, loaderClass ) {
+
+		this.geometryHandlers[ typeID ] = { "loaderClass": loaderClass };
+
+	},
+
+	addHierarchyHandler: function ( typeID, loaderClass ) {
+
+		this.hierarchyHandlers[ typeID ] = { "loaderClass": loaderClass };
+
+	},
+
+	parse: function ( json, callbackFinished, url ) {
+
+		var scope = this;
+
+		var urlBase = THREE.Loader.prototype.extractUrlBase( url );
+
+		var geometry, material, camera, fog,
+			texture, images, color,
+			light, hex, intensity,
+			counter_models, counter_textures,
+			total_models, total_textures,
+			result;
+
+		var target_array = [];
+
+		var data = json;
+
+		// async geometry loaders
+
+		for ( var typeID in this.geometryHandlers ) {
+
+			var loaderClass = this.geometryHandlers[ typeID ][ "loaderClass" ];
+			this.geometryHandlers[ typeID ][ "loaderObject" ] = new loaderClass();
+
+		}
+
+		// async hierachy loaders
+
+		for ( var typeID in this.hierarchyHandlers ) {
+
+			var loaderClass = this.hierarchyHandlers[ typeID ][ "loaderClass" ];
+			this.hierarchyHandlers[ typeID ][ "loaderObject" ] = new loaderClass();
+
+		}
+
+		counter_models = 0;
+		counter_textures = 0;
+
+		result = {
+
+			scene: new THREE.Scene(),
+			geometries: {},
+			face_materials: {},
+			materials: {},
+			textures: {},
+			objects: {},
+			cameras: {},
+			lights: {},
+			fogs: {},
+			empties: {},
+			groups: {}
+
+		};
+
+		if ( data.transform ) {
+
+			var position = data.transform.position,
+				rotation = data.transform.rotation,
+				scale = data.transform.scale;
+
+			if ( position ) {
+
+				result.scene.position.fromArray( position );
+
+			}
+
+			if ( rotation ) {
+
+				result.scene.rotation.fromArray( rotation );
+
+			}
+
+			if ( scale ) {
+
+				result.scene.scale.fromArray( scale );
+
+			}
+
+			if ( position || rotation || scale ) {
+
+				result.scene.updateMatrix();
+				result.scene.updateMatrixWorld();
+
+			}
+
+		}
+
+		function get_url( source_url, url_type ) {
+
+			if ( url_type == "relativeToHTML" ) {
+
+				return source_url;
+
+			} else {
+
+				return urlBase + source_url;
+
+			}
+
+		};
+
+		// toplevel loader function, delegates to handle_children
+
+		function handle_objects() {
+
+			handle_children( result.scene, data.objects );
+
+		}
+
+		// handle all the children from the loaded json and attach them to given parent
+
+		function handle_children( parent, children ) {
+
+			var mat, dst, pos, rot, scl, quat;
+
+			for ( var objID in children ) {
+
+				// check by id if child has already been handled,
+				// if not, create new object
+
+				var object = result.objects[ objID ];
+				var objJSON = children[ objID ];
+
+				if ( object === undefined ) {
+
+					// meshes
+
+					if ( objJSON.type && ( objJSON.type in scope.hierarchyHandlers ) ) {
+
+						if ( objJSON.loading === undefined ) {
+
+							var reservedTypes = {
+								"type": 1, "url": 1, "material": 1,
+								"position": 1, "rotation": 1, "scale" : 1,
+								"visible": 1, "children": 1, "userData": 1,
+								"skin": 1, "morph": 1, "mirroredLoop": 1, "duration": 1
+							};
+
+							var loaderParameters = {};
+
+							for ( var parType in objJSON ) {
+
+								if ( ! ( parType in reservedTypes ) ) {
+
+									loaderParameters[ parType ] = objJSON[ parType ];
+
+								}
+
+							}
+
+							material = result.materials[ objJSON.material ];
+
+							objJSON.loading = true;
+
+							var loader = scope.hierarchyHandlers[ objJSON.type ][ "loaderObject" ];
+
+							// ColladaLoader
+
+							if ( loader.options ) {
+
+								loader.load( get_url( objJSON.url, data.urlBaseType ), create_callback_hierachy( objID, parent, material, objJSON ) );
+
+							// UTF8Loader
+							// OBJLoader
+
+							} else {
+
+								loader.load( get_url( objJSON.url, data.urlBaseType ), create_callback_hierachy( objID, parent, material, objJSON ), loaderParameters );
+
+							}
+
+						}
+
+					} else if ( objJSON.geometry !== undefined ) {
+
+						geometry = result.geometries[ objJSON.geometry ];
+
+						// geometry already loaded
+
+						if ( geometry ) {
+
+							var needsTangents = false;
+
+							material = result.materials[ objJSON.material ];
+							needsTangents = material instanceof THREE.ShaderMaterial;
+
+							pos = objJSON.position;
+							rot = objJSON.rotation;
+							scl = objJSON.scale;
+							mat = objJSON.matrix;
+							quat = objJSON.quaternion;
+
+							// use materials from the model file
+							// if there is no material specified in the object
+
+							if ( ! objJSON.material ) {
+
+								material = new THREE.MeshFaceMaterial( result.face_materials[ objJSON.geometry ] );
+
+							}
+
+							// use materials from the model file
+							// if there is just empty face material
+							// (must create new material as each model has its own face material)
+
+							if ( ( material instanceof THREE.MeshFaceMaterial ) && material.materials.length === 0 ) {
+
+								material = new THREE.MeshFaceMaterial( result.face_materials[ objJSON.geometry ] );
+
+							}
+
+							if ( material instanceof THREE.MeshFaceMaterial ) {
+
+								for ( var i = 0; i < material.materials.length; i ++ ) {
+
+									needsTangents = needsTangents || ( material.materials[ i ] instanceof THREE.ShaderMaterial );
+
+								}
+
+							}
+
+							if ( needsTangents ) {
+
+								geometry.computeTangents();
+
+							}
+
+							if ( objJSON.skin ) {
+
+								object = new THREE.SkinnedMesh( geometry, material );
+
+							} else if ( objJSON.morph ) {
+
+								object = new THREE.MorphAnimMesh( geometry, material );
+
+								if ( objJSON.duration !== undefined ) {
+
+									object.duration = objJSON.duration;
+
+								}
+
+								if ( objJSON.time !== undefined ) {
+
+									object.time = objJSON.time;
+
+								}
+
+								if ( objJSON.mirroredLoop !== undefined ) {
+
+									object.mirroredLoop = objJSON.mirroredLoop;
+
+								}
+
+								if ( material.morphNormals ) {
+
+									geometry.computeMorphNormals();
+
+								}
+
+							} else {
+
+								object = new THREE.Mesh( geometry, material );
+
+							}
+
+							object.name = objID;
+
+							if ( mat ) {
+
+								object.matrixAutoUpdate = false;
+								object.matrix.set(
+									mat[0],  mat[1],  mat[2],  mat[3],
+									mat[4],  mat[5],  mat[6],  mat[7],
+									mat[8],  mat[9],  mat[10], mat[11],
+									mat[12], mat[13], mat[14], mat[15]
+								);
+
+							} else {
+
+								object.position.fromArray( pos );
+
+								if ( quat ) {
+
+									object.quaternion.fromArray( quat );
+
+								} else {
+
+									object.rotation.fromArray( rot );
+
+								}
+
+								object.scale.fromArray( scl );
+
+							}
+
+							object.visible = objJSON.visible;
+							object.castShadow = objJSON.castShadow;
+							object.receiveShadow = objJSON.receiveShadow;
+
+							parent.add( object );
+
+							result.objects[ objID ] = object;
+
+						}
+
+					// lights
+
+					} else if ( objJSON.type === "AmbientLight" || objJSON.type === "PointLight" ||
+						objJSON.type === "DirectionalLight" || objJSON.type === "SpotLight" ||
+						objJSON.type === "HemisphereLight" || objJSON.type === "AreaLight" ) {
+
+						var color = objJSON.color;
+						var intensity = objJSON.intensity;
+						var distance = objJSON.distance;
+						var position = objJSON.position;
+						var rotation = objJSON.rotation;
+
+						switch ( objJSON.type ) {
+
+							case 'AmbientLight':
+								light = new THREE.AmbientLight( color );
+								break;
+
+							case 'PointLight':
+								light = new THREE.PointLight( color, intensity, distance );
+								light.position.fromArray( position );
+								break;
+
+							case 'DirectionalLight':
+								light = new THREE.DirectionalLight( color, intensity );
+								light.position.fromArray( objJSON.direction );
+								break;
+
+							case 'SpotLight':
+								light = new THREE.SpotLight( color, intensity, distance, 1 );
+								light.angle = objJSON.angle;
+								light.position.fromArray( position );
+								light.target.set( position[ 0 ], position[ 1 ] - distance, position[ 2 ] );
+								light.target.applyEuler( new THREE.Euler( rotation[ 0 ], rotation[ 1 ], rotation[ 2 ], 'XYZ' ) );
+								break;
+
+							case 'HemisphereLight':
+								light = new THREE.DirectionalLight( color, intensity, distance );
+								light.target.set( position[ 0 ], position[ 1 ] - distance, position[ 2 ] );
+								light.target.applyEuler( new THREE.Euler( rotation[ 0 ], rotation[ 1 ], rotation[ 2 ], 'XYZ' ) );
+								break;
+
+							case 'AreaLight':
+								light = new THREE.AreaLight(color, intensity);
+								light.position.fromArray( position );
+								light.width = objJSON.size;
+								light.height = objJSON.size_y;
+								break;
+
+						}
+
+						parent.add( light );
+
+						light.name = objID;
+						result.lights[ objID ] = light;
+						result.objects[ objID ] = light;
+
+					// cameras
+
+					} else if ( objJSON.type === "PerspectiveCamera" || objJSON.type === "OrthographicCamera" ) {
+
+						pos = objJSON.position;
+						rot = objJSON.rotation;
+						quat = objJSON.quaternion;
+
+						if ( objJSON.type === "PerspectiveCamera" ) {
+
+							camera = new THREE.PerspectiveCamera( objJSON.fov, objJSON.aspect, objJSON.near, objJSON.far );
+
+						} else if ( objJSON.type === "OrthographicCamera" ) {
+
+							camera = new THREE.OrthographicCamera( objJSON.left, objJSON.right, objJSON.top, objJSON.bottom, objJSON.near, objJSON.far );
+
+						}
+
+						camera.name = objID;
+						camera.position.fromArray( pos );
+
+						if ( quat !== undefined ) {
+
+							camera.quaternion.fromArray( quat );
+
+						} else if ( rot !== undefined ) {
+
+							camera.rotation.fromArray( rot );
+
+						}
+
+						parent.add( camera );
+
+						result.cameras[ objID ] = camera;
+						result.objects[ objID ] = camera;
+
+					// pure Object3D
+
+					} else {
+
+						pos = objJSON.position;
+						rot = objJSON.rotation;
+						scl = objJSON.scale;
+						quat = objJSON.quaternion;
+
+						object = new THREE.Object3D();
+						object.name = objID;
+						object.position.fromArray( pos );
+
+						if ( quat ) {
+
+							object.quaternion.fromArray( quat );
+
+						} else {
+
+							object.rotation.fromArray( rot );
+
+						}
+
+						object.scale.fromArray( scl );
+						object.visible = ( objJSON.visible !== undefined ) ? objJSON.visible : false;
+
+						parent.add( object );
+
+						result.objects[ objID ] = object;
+						result.empties[ objID ] = object;
+
+					}
+
+					if ( object ) {
+
+						if ( objJSON.userData !== undefined ) {
+
+							for ( var key in objJSON.userData ) {
+
+								var value = objJSON.userData[ key ];
+								object.userData[ key ] = value;
+
+							}
+
+						}
+
+						if ( objJSON.groups !== undefined ) {
+
+							for ( var i = 0; i < objJSON.groups.length; i ++ ) {
+
+								var groupID = objJSON.groups[ i ];
+
+								if ( result.groups[ groupID ] === undefined ) {
+
+									result.groups[ groupID ] = [];
+
+								}
+
+								result.groups[ groupID ].push( objID );
+
+							}
+
+						}
+
+					}
+
+				}
+
+				if ( object !== undefined && objJSON.children !== undefined ) {
+
+					handle_children( object, objJSON.children );
+
+				}
+
+			}
+
+		};
+
+		function handle_mesh( geo, mat, id ) {
+
+			result.geometries[ id ] = geo;
+			result.face_materials[ id ] = mat;
+			handle_objects();
+
+		};
+
+		function handle_hierarchy( node, id, parent, material, obj ) {
+
+			var p = obj.position;
+			var r = obj.rotation;
+			var q = obj.quaternion;
+			var s = obj.scale;
+
+			node.position.fromArray( p );
+
+			if ( q ) {
+
+				node.quaternion.fromArray( q );
+
+			} else {
+
+				node.rotation.fromArray( r );
+
+			}
+
+			node.scale.fromArray( s );
+
+			// override children materials
+			// if object material was specified in JSON explicitly
+
+			if ( material ) {
+
+				node.traverse( function ( child ) {
+
+					child.material = material;
+
+				} );
+
+			}
+
+			// override children visibility
+			// with root node visibility as specified in JSON
+
+			var visible = ( obj.visible !== undefined ) ? obj.visible : true;
+
+			node.traverse( function ( child ) {
+
+				child.visible = visible;
+
+			} );
+
+			parent.add( node );
+
+			node.name = id;
+
+			result.objects[ id ] = node;
+			handle_objects();
+
+		};
+
+		function create_callback_geometry( id ) {
+
+			return function ( geo, mat ) {
+
+				geo.name = id;
+
+				handle_mesh( geo, mat, id );
+
+				counter_models -= 1;
+
+				scope.onLoadComplete();
+
+				async_callback_gate();
+
+			}
+
+		};
+
+		function create_callback_hierachy( id, parent, material, obj ) {
+
+			return function ( event ) {
+
+				var result;
+
+				// loaders which use EventDispatcher
+
+				if ( event.content ) {
+
+					result = event.content;
+
+				// ColladaLoader
+
+				} else if ( event.dae ) {
+
+					result = event.scene;
+
+
+				// UTF8Loader
+
+				} else {
+
+					result = event;
+
+				}
+
+				handle_hierarchy( result, id, parent, material, obj );
+
+				counter_models -= 1;
+
+				scope.onLoadComplete();
+
+				async_callback_gate();
+
+			}
+
+		};
+
+		function create_callback_embed( id ) {
+
+			return function ( geo, mat ) {
+
+				geo.name = id;
+
+				result.geometries[ id ] = geo;
+				result.face_materials[ id ] = mat;
+
+			}
+
+		};
+
+		function async_callback_gate() {
+
+			var progress = {
+
+				totalModels : total_models,
+				totalTextures : total_textures,
+				loadedModels : total_models - counter_models,
+				loadedTextures : total_textures - counter_textures
+
+			};
+
+			scope.callbackProgress( progress, result );
+
+			scope.onLoadProgress();
+
+			if ( counter_models === 0 && counter_textures === 0 ) {
+
+				finalize();
+				callbackFinished( result );
+
+			}
+
+		};
+
+		function finalize() {
+
+			// take care of targets which could be asynchronously loaded objects
+
+			for ( var i = 0; i < target_array.length; i ++ ) {
+
+				var ta = target_array[ i ];
+
+				var target = result.objects[ ta.targetName ];
+
+				if ( target ) {
+
+					ta.object.target = target;
+
+				} else {
+
+					// if there was error and target of specified name doesn't exist in the scene file
+					// create instead dummy target
+					// (target must be added to scene explicitly as parent is already added)
+
+					ta.object.target = new THREE.Object3D();
+					result.scene.add( ta.object.target );
+
+				}
+
+				ta.object.target.userData.targetInverse = ta.object;
+
+			}
+
+		};
+
+		var callbackTexture = function ( count ) {
+
+			counter_textures -= count;
+			async_callback_gate();
+
+			scope.onLoadComplete();
+
+		};
+
+		// must use this instead of just directly calling callbackTexture
+		// because of closure in the calling context loop
+
+		var generateTextureCallback = function ( count ) {
+
+			return function () {
+
+				callbackTexture( count );
+
+			};
+
+		};
+
+		function traverse_json_hierarchy( objJSON, callback ) {
+
+			callback( objJSON );
+
+			if ( objJSON.children !== undefined ) {
+
+				for ( var objChildID in objJSON.children ) {
+
+					traverse_json_hierarchy( objJSON.children[ objChildID ], callback );
+
+				}
+
+			}
+
+		};
+
+		// first go synchronous elements
+
+		// fogs
+
+		var fogID, fogJSON;
+
+		for ( fogID in data.fogs ) {
+
+			fogJSON = data.fogs[ fogID ];
+
+			if ( fogJSON.type === "linear" ) {
+
+				fog = new THREE.Fog( 0x000000, fogJSON.near, fogJSON.far );
+
+			} else if ( fogJSON.type === "exp2" ) {
+
+				fog = new THREE.FogExp2( 0x000000, fogJSON.density );
+
+			}
+
+			color = fogJSON.color;
+			fog.color.setRGB( color[0], color[1], color[2] );
+
+			result.fogs[ fogID ] = fog;
+
+		}
+
+		// now come potentially asynchronous elements
+
+		// geometries
+
+		// count how many geometries will be loaded asynchronously
+
+		var geoID, geoJSON;
+
+		for ( geoID in data.geometries ) {
+
+			geoJSON = data.geometries[ geoID ];
+
+			if ( geoJSON.type in this.geometryHandlers ) {
+
+				counter_models += 1;
+
+				scope.onLoadStart();
+
+			}
+
+		}
+
+		// count how many hierarchies will be loaded asynchronously
+
+		for ( var objID in data.objects ) {
+
+			traverse_json_hierarchy( data.objects[ objID ], function ( objJSON ) {
+
+				if ( objJSON.type && ( objJSON.type in scope.hierarchyHandlers ) ) {
+
+					counter_models += 1;
+
+					scope.onLoadStart();
+
+				}
+
+			});
+
+		}
+
+		total_models = counter_models;
+
+		for ( geoID in data.geometries ) {
+
+			geoJSON = data.geometries[ geoID ];
+
+			if ( geoJSON.type === "cube" ) {
+
+				geometry = new THREE.BoxGeometry( geoJSON.width, geoJSON.height, geoJSON.depth, geoJSON.widthSegments, geoJSON.heightSegments, geoJSON.depthSegments );
+				geometry.name = geoID;
+				result.geometries[ geoID ] = geometry;
+
+			} else if ( geoJSON.type === "plane" ) {
+
+				geometry = new THREE.PlaneGeometry( geoJSON.width, geoJSON.height, geoJSON.widthSegments, geoJSON.heightSegments );
+				geometry.name = geoID;
+				result.geometries[ geoID ] = geometry;
+
+			} else if ( geoJSON.type === "sphere" ) {
+
+				geometry = new THREE.SphereGeometry( geoJSON.radius, geoJSON.widthSegments, geoJSON.heightSegments );
+				geometry.name = geoID;
+				result.geometries[ geoID ] = geometry;
+
+			} else if ( geoJSON.type === "cylinder" ) {
+
+				geometry = new THREE.CylinderGeometry( geoJSON.topRad, geoJSON.botRad, geoJSON.height, geoJSON.radSegs, geoJSON.heightSegs );
+				geometry.name = geoID;
+				result.geometries[ geoID ] = geometry;
+
+			} else if ( geoJSON.type === "torus" ) {
+
+				geometry = new THREE.TorusGeometry( geoJSON.radius, geoJSON.tube, geoJSON.segmentsR, geoJSON.segmentsT );
+				geometry.name = geoID;
+				result.geometries[ geoID ] = geometry;
+
+			} else if ( geoJSON.type === "icosahedron" ) {
+
+				geometry = new THREE.IcosahedronGeometry( geoJSON.radius, geoJSON.subdivisions );
+				geometry.name = geoID;
+				result.geometries[ geoID ] = geometry;
+
+			} else if ( geoJSON.type in this.geometryHandlers ) {
+
+				var loaderParameters = {};
+
+				for ( var parType in geoJSON ) {
+
+					if ( parType !== "type" && parType !== "url" ) {
+
+						loaderParameters[ parType ] = geoJSON[ parType ];
+
+					}
+
+				}
+
+				var loader = this.geometryHandlers[ geoJSON.type ][ "loaderObject" ];
+				loader.load( get_url( geoJSON.url, data.urlBaseType ), create_callback_geometry( geoID ), loaderParameters );
+
+			} else if ( geoJSON.type === "embedded" ) {
+
+				var modelJson = data.embeds[ geoJSON.id ],
+					texture_path = "";
+
+				// pass metadata along to jsonLoader so it knows the format version
+
+				modelJson.metadata = data.metadata;
+
+				if ( modelJson ) {
+
+					var jsonLoader = this.geometryHandlers[ "ascii" ][ "loaderObject" ];
+					var model = jsonLoader.parse( modelJson, texture_path );
+					create_callback_embed( geoID )( model.geometry, model.materials );
+
+				}
+
+			}
+
+		}
+
+		// textures
+
+		// count how many textures will be loaded asynchronously
+
+		var textureID, textureJSON;
+
+		for ( textureID in data.textures ) {
+
+			textureJSON = data.textures[ textureID ];
+
+			if ( textureJSON.url instanceof Array ) {
+
+				counter_textures += textureJSON.url.length;
+
+				for( var n = 0; n < textureJSON.url.length; n ++ ) {
+
+					scope.onLoadStart();
+
+				}
+
+			} else {
+
+				counter_textures += 1;
+
+				scope.onLoadStart();
+
+			}
+
+		}
+
+		total_textures = counter_textures;
+
+		for ( textureID in data.textures ) {
+
+			textureJSON = data.textures[ textureID ];
+
+			if ( textureJSON.mapping !== undefined && THREE[ textureJSON.mapping ] !== undefined ) {
+
+				textureJSON.mapping = new THREE[ textureJSON.mapping ]();
+
+			}
+
+			var texture;
+
+			if ( textureJSON.url instanceof Array ) {
+
+				var count = textureJSON.url.length;
+				var url_array = [];
+
+				for ( var i = 0; i < count; i ++ ) {
+
+					url_array[ i ] = get_url( textureJSON.url[ i ], data.urlBaseType );
+
+				}
+
+				var loader = THREE.Loader.Handlers.get( url_array[ 0 ] );
+
+				if ( loader !== null ) {
+
+					texture = loader.load( url_array, generateTextureCallback( count ) );
+					texture.mapping = textureJSON.mapping;
+
+				} else {
+
+					texture = THREE.ImageUtils.loadTextureCube( url_array, textureJSON.mapping, generateTextureCallback( count ) );
+
+				}
+
+			} else {
+
+				var fullUrl = get_url( textureJSON.url, data.urlBaseType );
+				var textureCallback = generateTextureCallback( 1 );
+
+				var loader = THREE.Loader.Handlers.get( fullUrl );
+
+				if ( loader !== null ) {
+
+					texture = loader.load( fullUrl, textureCallback );
+
+				} else {
+
+					texture = new THREE.Texture();
+					loader = new THREE.ImageLoader();
+					
+					( function ( texture ) {
+
+						loader.load( fullUrl, function ( image ) {
+
+							texture.image = image;
+							texture.needsUpdate = true;
+
+							textureCallback();
+
+						} );
+					
+					} )( texture )
+					
+
+				}
+
+				texture.mapping = textureJSON.mapping;
+
+				if ( THREE[ textureJSON.minFilter ] !== undefined )
+					texture.minFilter = THREE[ textureJSON.minFilter ];
+
+				if ( THREE[ textureJSON.magFilter ] !== undefined )
+					texture.magFilter = THREE[ textureJSON.magFilter ];
+
+				if ( textureJSON.anisotropy ) texture.anisotropy = textureJSON.anisotropy;
+
+				if ( textureJSON.repeat ) {
+
+					texture.repeat.set( textureJSON.repeat[ 0 ], textureJSON.repeat[ 1 ] );
+
+					if ( textureJSON.repeat[ 0 ] !== 1 ) texture.wrapS = THREE.RepeatWrapping;
+					if ( textureJSON.repeat[ 1 ] !== 1 ) texture.wrapT = THREE.RepeatWrapping;
+
+				}
+
+				if ( textureJSON.offset ) {
+
+					texture.offset.set( textureJSON.offset[ 0 ], textureJSON.offset[ 1 ] );
+
+				}
+
+				// handle wrap after repeat so that default repeat can be overriden
+
+				if ( textureJSON.wrap ) {
+
+					var wrapMap = {
+						"repeat": THREE.RepeatWrapping,
+						"mirror": THREE.MirroredRepeatWrapping
+					}
+
+					if ( wrapMap[ textureJSON.wrap[ 0 ] ] !== undefined ) texture.wrapS = wrapMap[ textureJSON.wrap[ 0 ] ];
+					if ( wrapMap[ textureJSON.wrap[ 1 ] ] !== undefined ) texture.wrapT = wrapMap[ textureJSON.wrap[ 1 ] ];
+
+				}
+
+			}
+
+			result.textures[ textureID ] = texture;
+
+		}
+
+		// materials
+
+		var matID, matJSON;
+		var parID;
+
+		for ( matID in data.materials ) {
+
+			matJSON = data.materials[ matID ];
+
+			for ( parID in matJSON.parameters ) {
+
+				if ( parID === "envMap" || parID === "map" || parID === "lightMap" || parID === "bumpMap" ) {
+
+					matJSON.parameters[ parID ] = result.textures[ matJSON.parameters[ parID ] ];
+
+				} else if ( parID === "shading" ) {
+
+					matJSON.parameters[ parID ] = ( matJSON.parameters[ parID ] === "flat" ) ? THREE.FlatShading : THREE.SmoothShading;
+
+				} else if ( parID === "side" ) {
+
+					if ( matJSON.parameters[ parID ] == "double" ) {
+
+						matJSON.parameters[ parID ] = THREE.DoubleSide;
+
+					} else if ( matJSON.parameters[ parID ] == "back" ) {
+
+						matJSON.parameters[ parID ] = THREE.BackSide;
+
+					} else {
+
+						matJSON.parameters[ parID ] = THREE.FrontSide;
+
+					}
+
+				} else if ( parID === "blending" ) {
+
+					matJSON.parameters[ parID ] = matJSON.parameters[ parID ] in THREE ? THREE[ matJSON.parameters[ parID ] ] : THREE.NormalBlending;
+
+				} else if ( parID === "combine" ) {
+
+					matJSON.parameters[ parID ] = matJSON.parameters[ parID ] in THREE ? THREE[ matJSON.parameters[ parID ] ] : THREE.MultiplyOperation;
+
+				} else if ( parID === "vertexColors" ) {
+
+					if ( matJSON.parameters[ parID ] == "face" ) {
+
+						matJSON.parameters[ parID ] = THREE.FaceColors;
+
+					// default to vertex colors if "vertexColors" is anything else face colors or 0 / null / false
+
+					} else if ( matJSON.parameters[ parID ] ) {
+
+						matJSON.parameters[ parID ] = THREE.VertexColors;
+
+					}
+
+				} else if ( parID === "wrapRGB" ) {
+
+					var v3 = matJSON.parameters[ parID ];
+					matJSON.parameters[ parID ] = new THREE.Vector3( v3[ 0 ], v3[ 1 ], v3[ 2 ] );
+
+				}
+
+			}
+
+			if ( matJSON.parameters.opacity !== undefined && matJSON.parameters.opacity < 1.0 ) {
+
+				matJSON.parameters.transparent = true;
+
+			}
+
+			if ( matJSON.parameters.normalMap ) {
+
+				var shader = THREE.ShaderLib[ "normalmap" ];
+				var uniforms = THREE.UniformsUtils.clone( shader.uniforms );
+
+				var diffuse = matJSON.parameters.color;
+				var specular = matJSON.parameters.specular;
+				var ambient = matJSON.parameters.ambient;
+				var shininess = matJSON.parameters.shininess;
+
+				uniforms[ "tNormal" ].value = result.textures[ matJSON.parameters.normalMap ];
+
+				if ( matJSON.parameters.normalScale ) {
+
+					uniforms[ "uNormalScale" ].value.set( matJSON.parameters.normalScale[ 0 ], matJSON.parameters.normalScale[ 1 ] );
+
+				}
+
+				if ( matJSON.parameters.map ) {
+
+					uniforms[ "tDiffuse" ].value = matJSON.parameters.map;
+					uniforms[ "enableDiffuse" ].value = true;
+
+				}
+
+				if ( matJSON.parameters.envMap ) {
+
+					uniforms[ "tCube" ].value = matJSON.parameters.envMap;
+					uniforms[ "enableReflection" ].value = true;
+					uniforms[ "reflectivity" ].value = matJSON.parameters.reflectivity;
+
+				}
+
+				if ( matJSON.parameters.lightMap ) {
+
+					uniforms[ "tAO" ].value = matJSON.parameters.lightMap;
+					uniforms[ "enableAO" ].value = true;
+
+				}
+
+				if ( matJSON.parameters.specularMap ) {
+
+					uniforms[ "tSpecular" ].value = result.textures[ matJSON.parameters.specularMap ];
+					uniforms[ "enableSpecular" ].value = true;
+
+				}
+
+				if ( matJSON.parameters.displacementMap ) {
+
+					uniforms[ "tDisplacement" ].value = result.textures[ matJSON.parameters.displacementMap ];
+					uniforms[ "enableDisplacement" ].value = true;
+
+					uniforms[ "uDisplacementBias" ].value = matJSON.parameters.displacementBias;
+					uniforms[ "uDisplacementScale" ].value = matJSON.parameters.displacementScale;
+
+				}
+
+				uniforms[ "diffuse" ].value.setHex( diffuse );
+				uniforms[ "specular" ].value.setHex( specular );
+				uniforms[ "ambient" ].value.setHex( ambient );
+
+				uniforms[ "shininess" ].value = shininess;
+
+				if ( matJSON.parameters.opacity ) {
+
+					uniforms[ "opacity" ].value = matJSON.parameters.opacity;
+
+				}
+
+				var parameters = { fragmentShader: shader.fragmentShader, vertexShader: shader.vertexShader, uniforms: uniforms, lights: true, fog: true };
+
+				material = new THREE.ShaderMaterial( parameters );
+
+			} else {
+
+				material = new THREE[ matJSON.type ]( matJSON.parameters );
+
+			}
+
+			material.name = matID;
+
+			result.materials[ matID ] = material;
+
+		}
+
+		// second pass through all materials to initialize MeshFaceMaterials
+		// that could be referring to other materials out of order
+
+		for ( matID in data.materials ) {
+
+			matJSON = data.materials[ matID ];
+
+			if ( matJSON.parameters.materials ) {
+
+				var materialArray = [];
+
+				for ( var i = 0; i < matJSON.parameters.materials.length; i ++ ) {
+
+					var label = matJSON.parameters.materials[ i ];
+					materialArray.push( result.materials[ label ] );
+
+				}
+
+				result.materials[ matID ].materials = materialArray;
+
+			}
+
+		}
+
+		// objects ( synchronous init of procedural primitives )
+
+		handle_objects();
+
+		// defaults
+
+		if ( result.cameras && data.defaults.camera ) {
+
+			result.currentCamera = result.cameras[ data.defaults.camera ];
+
+		}
+
+		if ( result.fogs && data.defaults.fog ) {
+
+			result.scene.fog = result.fogs[ data.defaults.fog ];
+
+		}
+
+		// synchronous callback
+
+		scope.callbackSync( result );
+
+		// just in case there are no async elements
+
+		async_callback_gate();
+
+	}
+
+}

+ 451 - 0
examples/js/loaders/TGALoader.js

@@ -0,0 +1,451 @@
+/*
+ * @author Daosheng Mu / https://github.com/DaoshengMu/
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.TGALoader = function () {};
+
+THREE.TGALoader.prototype = {
+
+	constructor: THREE.TGALoader,
+
+	load: function ( url, onLoad, onError ) {
+		
+		var scope = this;
+		
+		var texture = new THREE.DataTexture();
+
+		var loader = new THREE.XHRLoader();
+		loader.setResponseType( 'arraybuffer' );
+		loader.load( url, function ( buffer ) {
+
+			texture.image = scope.parse( buffer );
+			texture.needsUpdate = true;
+
+			if ( onLoad ) onLoad( texture );
+
+		} );
+
+		return texture;
+
+	},
+
+	// reference from vthibault, https://github.com/vthibault/roBrowser/blob/master/src/Loaders/Targa.js
+	parse: function ( buffer ) {
+			   
+		// TGA Constants
+		var TGA_TYPE_NO_DATA = 0,
+		TGA_TYPE_INDEXED = 1,
+		TGA_TYPE_RGB = 2,
+		TGA_TYPE_GREY = 3,
+		TGA_TYPE_RLE_INDEXED = 9,
+		TGA_TYPE_RLE_RGB = 10,
+		TGA_TYPE_RLE_GREY = 11,
+
+		TGA_ORIGIN_MASK = 0x30,
+		TGA_ORIGIN_SHIFT = 0x04,
+		TGA_ORIGIN_BL = 0x00,
+		TGA_ORIGIN_BR = 0x01,
+		TGA_ORIGIN_UL = 0x02,
+		TGA_ORIGIN_UR = 0x03;
+
+		
+		if ( buffer.length < 19 )
+			console.error( 'THREE.TGALoader.parse: Not enough data to contain header.' );
+		
+		var content = new Uint8Array( buffer ),
+			offset = 0,
+			header = {
+				id_length:       content[ offset ++ ], 
+				colormap_type:   content[ offset ++ ],
+				image_type:      content[ offset ++ ],
+				colormap_index:  content[ offset ++ ] | content[ offset ++ ] << 8,
+				colormap_length: content[ offset ++ ] | content[ offset ++ ] << 8,
+				colormap_size:   content[ offset ++ ],
+
+				origin: [
+					content[ offset ++ ] | content[ offset ++ ] << 8,
+					content[ offset ++ ] | content[ offset ++ ] << 8
+				],
+				width:      content[ offset ++ ] | content[ offset ++ ] << 8,
+				height:     content[ offset ++ ] | content[ offset ++ ] << 8,
+				pixel_size: content[ offset ++ ],
+				flags:      content[ offset ++ ]
+			};
+				
+		function tgaCheckHeader( header ) {
+
+			switch( header.image_type ) {
+
+				// Check indexed type
+				case TGA_TYPE_INDEXED:
+				case TGA_TYPE_RLE_INDEXED:
+					if ( header.colormap_length > 256 || header.colormap_size !== 24 || header.colormap_type !== 1) {
+						console.error('THREE.TGALoader.parse.tgaCheckHeader: Invalid type colormap data for indexed type');
+					}
+					break;
+
+				// Check colormap type
+				case TGA_TYPE_RGB:
+				case TGA_TYPE_GREY:
+				case TGA_TYPE_RLE_RGB:
+				case TGA_TYPE_RLE_GREY:
+					if (header.colormap_type) {
+						console.error('THREE.TGALoader.parse.tgaCheckHeader: Invalid type colormap data for colormap type');
+					}
+					break;
+
+				// What the need of a file without data ?
+				case TGA_TYPE_NO_DATA:
+					console.error('THREE.TGALoader.parse.tgaCheckHeader: No data');
+
+				// Invalid type ?
+				default:
+					console.error('THREE.TGALoader.parse.tgaCheckHeader: Invalid type " '+ header.image_type + '"');
+
+			}
+
+			// Check image width and height
+			if ( header.width <= 0 || header.height <=0 ) {
+				console.error( 'THREE.TGALoader.parse.tgaCheckHeader: Invalid image size' );
+			}
+
+			// Check image pixel size 
+			if (header.pixel_size !== 8  &&
+				header.pixel_size !== 16 &&
+				header.pixel_size !== 24 &&
+				header.pixel_size !== 32) {
+				console.error('THREE.TGALoader.parse.tgaCheckHeader: Invalid pixel size "' + header.pixel_size + '"');
+			}
+
+		}
+
+		// Check tga if it is valid format
+		tgaCheckHeader( header );
+
+		if ( header.id_length + offset > buffer.length ) {
+			console.error('THREE.TGALoader.parse: No data');
+		}
+
+		// Skip the needn't data
+		offset += header.id_length;
+
+		// Get targa information about RLE compression and palette
+		var use_rle = false, 
+			use_pal = false, 
+			use_grey = false;
+	
+		switch ( header.image_type ) {
+
+			case TGA_TYPE_RLE_INDEXED:
+				use_rle = true;
+				use_pal = true;
+				break;
+
+			case TGA_TYPE_INDEXED:
+				use_pal = true;
+				break;
+				
+			case TGA_TYPE_RLE_RGB:
+				use_rle = true;
+				break;
+
+			case TGA_TYPE_RGB:
+				break;
+
+			case TGA_TYPE_RLE_GREY:
+				use_rle = true;
+				use_grey = true;
+				break;
+
+			case TGA_TYPE_GREY:
+				use_grey = true;
+				break;
+
+		}
+		
+		// Parse tga image buffer
+		function tgaParse( use_rle, use_pal, header, offset, data ) {
+			
+			var pixel_data,
+				pixel_size,
+				pixel_total,
+				palettes;
+		
+				pixel_size = header.pixel_size >> 3;
+				pixel_total = header.width * header.height * pixel_size;
+				
+			 // Read palettes
+			 if ( use_pal ) {
+				 palettes = data.subarray( offset, offset += header.colormap_length * ( header.colormap_size >> 3 ) );
+			 }
+			 
+			 // Read RLE
+			 if ( use_rle ) {
+				 pixel_data = new Uint8Array(pixel_total);
+				 
+				var c, count, i;
+				var shift = 0;
+				var pixels = new Uint8Array(pixel_size);
+
+				while (shift < pixel_total) {
+					c     = data[offset++];
+					count = (c & 0x7f) + 1;
+
+					// RLE pixels.
+					if (c & 0x80) {
+						// Bind pixel tmp array
+						for (i = 0; i < pixel_size; ++i) {
+								pixels[i] = data[offset++];
+						}
+
+						// Copy pixel array
+						for (i = 0; i < count; ++i) {
+								pixel_data.set(pixels, shift + i * pixel_size);
+						}
+
+						shift += pixel_size * count;
+
+					} else {
+						// Raw pixels.
+						count *= pixel_size;
+						for (i = 0; i < count; ++i) {
+								pixel_data[shift + i] = data[offset++];
+						}
+						shift += count;
+					}
+				}
+			 } else {
+				// RAW Pixels
+				pixel_data = data.subarray(
+					 offset, offset += (use_pal ? header.width * header.height : pixel_total)
+				);
+			 }
+			
+			 return { 
+				pixel_data: pixel_data, 
+				palettes: palettes 
+			 };
+		}
+		
+		function tgaGetImageData8bits(imageData, y_start, y_step, y_end, x_start, x_step, x_end, image, palettes) {
+
+			var colormap = palettes;
+			var color, i = 0, x, y;
+					var width = header.width;
+					
+			for (y = y_start; y !== y_end; y += y_step) {
+				for (x = x_start; x !== x_end; x += x_step, i++) {
+					color = image[i];
+					imageData[(x + width * y) * 4 + 3] = 255;
+					imageData[(x + width * y) * 4 + 2] = colormap[(color * 3) + 0];
+					imageData[(x + width * y) * 4 + 1] = colormap[(color * 3) + 1];
+					imageData[(x + width * y) * 4 + 0] = colormap[(color * 3) + 2];
+				}
+			}
+
+			return imageData;
+
+		};
+
+		function tgaGetImageData16bits(imageData, y_start, y_step, y_end, x_start, x_step, x_end, image) {
+
+			var color, i = 0, x, y;
+			var width = header.width;
+
+			for (y = y_start; y !== y_end; y += y_step) {
+				for (x = x_start; x !== x_end; x += x_step, i += 2) {
+					color = image[i + 0] + (image[i + 1] << 8); // Inversed ?
+					imageData[(x + width * y) * 4 + 0] = (color & 0x7C00) >> 7;
+					imageData[(x + width * y) * 4 + 1] = (color & 0x03E0) >> 2;
+					imageData[(x + width * y) * 4 + 2] = (color & 0x001F) >> 3;
+					imageData[(x + width * y) * 4 + 3] = (color & 0x8000) ? 0 : 255;
+				}
+			}
+
+			return imageData;
+
+		};
+
+		function tgaGetImageData24bits(imageData, y_start, y_step, y_end, x_start, x_step, x_end, image) {
+
+			var i = 0, x, y;
+			var width = header.width;
+
+			for (y = y_start; y !== y_end; y += y_step) {
+				for (x = x_start; x !== x_end; x += x_step, i += 3) {
+					imageData[(x + width * y) * 4 + 3] = 255;
+					imageData[(x + width * y) * 4 + 2] = image[i + 0];
+					imageData[(x + width * y) * 4 + 1] = image[i + 1];
+					imageData[(x + width * y) * 4 + 0] = image[i + 2];
+				}
+			}
+
+			return imageData;
+
+		};
+		
+		function tgaGetImageData32bits(imageData, y_start, y_step, y_end, x_start, x_step, x_end, image) {		
+
+			var i = 0, x, y;
+			var width = header.width;
+
+			for (y = y_start; y !== y_end; y += y_step) {
+				for (x = x_start; x !== x_end; x += x_step, i += 4) {
+					imageData[(x + width * y) * 4 + 2] = image[i + 0];
+					imageData[(x + width * y) * 4 + 1] = image[i + 1];
+					imageData[(x + width * y) * 4 + 0] = image[i + 2];
+					imageData[(x + width * y) * 4 + 3] = image[i + 3];
+				}
+			}
+
+			return imageData;
+
+		};
+
+		function tgaGetImageDataGrey8bits( imageData, y_start, y_step, y_end, x_start, x_step, x_end, image ) {
+
+			var color, i = 0, x, y;
+			var width = header.width;
+
+			for (y = y_start; y !== y_end; y += y_step) {
+				for (x = x_start; x !== x_end; x += x_step, i++) {
+					color = image[i];
+					imageData[(x + width * y) * 4 + 0] = color;
+					imageData[(x + width * y) * 4 + 1] = color;
+					imageData[(x + width * y) * 4 + 2] = color;
+					imageData[(x + width * y) * 4 + 3] = 255;
+				}
+			}
+
+			return imageData;
+
+		};
+
+		function tgaGetImageDataGrey16bits(imageData, y_start, y_step, y_end, x_start, x_step, x_end, image) {		
+
+			var i = 0, x, y;
+			var width = header.width;
+
+			for (y = y_start; y !== y_end; y += y_step) {
+				for (x = x_start; x !== x_end; x += x_step, i += 2) {
+					imageData[(x + width * y) * 4 + 0] = image[i + 0];
+					imageData[(x + width * y) * 4 + 1] = image[i + 0];
+					imageData[(x + width * y) * 4 + 2] = image[i + 0];
+					imageData[(x + width * y) * 4 + 3] = image[i + 1];
+				}
+			}
+
+			return imageData;
+
+		};
+	
+		function getTgaRGBA( width, height, image, palette ) {
+
+			var x_start,
+				y_start,
+				x_step,
+				y_step,
+				x_end,
+				y_end,                    
+				data = new Uint8Array(width * height * 4);
+				
+			switch( (header.flags & TGA_ORIGIN_MASK) >> TGA_ORIGIN_SHIFT ) {
+				default:
+				case TGA_ORIGIN_UL:
+					x_start = 0;
+					x_step = 1;
+					x_end = width;
+					y_start = 0;
+					y_step = 1;
+					y_end = height;
+					break;
+					
+				case TGA_ORIGIN_BL:
+					x_start = 0;
+					x_step = 1;
+					x_end = width;
+					y_start = height - 1;
+					y_step = -1;
+					y_end = -1;
+					break;
+					
+				case TGA_ORIGIN_UR:
+					x_start = width - 1;
+					x_step = -1;
+					x_end = -1;
+					y_start = 0;
+					y_step = 1;
+					y_end = height;
+					break;
+					
+				case TGA_ORIGIN_BR:
+					x_start = width - 1;
+					x_step = -1;
+					x_end = -1;
+					y_start = height - 1;
+					y_step = -1;
+					y_end = -1;
+					break;
+
+			}	
+				
+			if ( use_grey ) {
+				
+				switch( header.pixel_size ) {
+					case 8:
+						tgaGetImageDataGrey8bits( data, y_start, y_step, y_end, x_start, x_step, x_end, image );
+						break;
+					case 16:
+						tgaGetImageDataGrey16bits( data, y_start, y_step, y_end, x_start, x_step, x_end, image );
+						break;
+					default:
+						console.error( 'THREE.TGALoader.parse.getTgaRGBA: not support this format' );
+						break;
+				}
+				
+			} else {
+				
+				switch( header.pixel_size ) {
+					case 8:
+						tgaGetImageData8bits( data, y_start, y_step, y_end, x_start, x_step, x_end, image, palette );
+						break;
+						
+					case 16:
+						tgaGetImageData16bits( data, y_start, y_step, y_end, x_start, x_step, x_end, image );
+						break;
+						
+					case 24:
+						tgaGetImageData24bits( data, y_start, y_step, y_end, x_start, x_step, x_end, image );
+						break;
+
+					case 32:
+						tgaGetImageData32bits( data, y_start, y_step, y_end, x_start, x_step, x_end, image );
+						break;
+						
+					default:
+						console.error( 'THREE.TGALoader.parse.getTgaRGBA: not support this format' );
+						break;  
+				}
+
+			}
+
+			// Load image data according to specific method
+			// var func = 'tgaGetImageData' + (use_grey ? 'Grey' : '') + (header.pixel_size) + 'bits';
+			// func(data, y_start, y_step, y_end, x_start, x_step, x_end, width, image, palette );
+			return data;
+
+		}
+		
+		var result = tgaParse( use_rle, use_pal, header, offset, content );
+		var rgbaData = getTgaRGBA( header.width, header.height, result.pixel_data, result.palettes );
+		
+		return {
+			width: header.width,
+			height: header.height,
+			data: rgbaData
+		};
+
+	}
+
+};

+ 18 - 22
examples/js/loaders/UTF8Loader.js

@@ -28,19 +28,15 @@ THREE.UTF8Loader.prototype.load = function ( jsonUrl, callback, options ) {
 THREE.UTF8Loader.BufferGeometryCreator = function () {
 };
 
-THREE.UTF8Loader.BufferGeometryCreator.prototype.create = function ( attribArray, indexArray ) {
+THREE.UTF8Loader.BufferGeometryCreator.prototype.create = function ( attribArray, indices ) {
 
-	var ntris = indexArray.length / 3;
+	var ntris = indices.length / 3;
 
 	var geometry = new THREE.BufferGeometry();
 
-    var positions = new THREE.Float32Attribute( ntris * 3, 3 );
-    var normals = new THREE.Float32Attribute( ntris * 3, 3 );
-    var uvs = new THREE.Float32Attribute( ntris * 3, 2 );
-
-	var positionsArray = positions.array;
-	var normalsArray = normals.array;
-	var uvsArray = uvs.array;
+	var positions = new Float32Array( ntris * 3 * 3 );
+	var normals = new Float32Array( ntris * 3 * 3 );
+	var uvs = new Float32Array( ntris * 3 * 2 );
 
 	var i, j, offset;
 	var x, y, z;
@@ -60,9 +56,9 @@ THREE.UTF8Loader.BufferGeometryCreator.prototype.create = function ( attribArray
 		y = attribArray[ i + 1 ];
 		z = attribArray[ i + 2 ];
 
-		positionsArray[ j++ ] = x;
-		positionsArray[ j++ ] = y;
-		positionsArray[ j++ ] = z;
+		positions[ j++ ] = x;
+		positions[ j++ ] = y;
+		positions[ j++ ] = z;
 
 	}
 
@@ -76,8 +72,8 @@ THREE.UTF8Loader.BufferGeometryCreator.prototype.create = function ( attribArray
 		u = attribArray[ i ];
 		v = attribArray[ i + 1 ];
 
-		uvsArray[ j++ ] = u;
-		uvsArray[ j++ ] = v;
+		uvs[ j++ ] = u;
+		uvs[ j++ ] = v;
 
 	}
 
@@ -92,18 +88,18 @@ THREE.UTF8Loader.BufferGeometryCreator.prototype.create = function ( attribArray
 		y = attribArray[ i + 1 ];
 		z = attribArray[ i + 2 ];
 
-		normalsArray[ j++ ] = x;
-		normalsArray[ j++ ] = y;
-		normalsArray[ j++ ] = z;
+		normals[ j++ ] = x;
+		normals[ j++ ] = y;
+		normals[ j++ ] = z;
 
 	}
 
-    geometry.addAttribute( 'index', indexArray, 1 );
-    geometry.addAttribute( 'position', positions );
-    geometry.addAttribute( 'normal', normals );
-    geometry.addAttribute( 'uv', uvs );
+    geometry.addAttribute( 'index', new THREE.BufferAttribute( indices, 1 ) );
+    geometry.addAttribute( 'position', new THREE.BufferAttribute( positions, 3 ) );
+    geometry.addAttribute( 'normal', new THREE.BufferAttribute( normals, 3 ) );
+    geometry.addAttribute( 'uv', new THREE.BufferAttribute( uvs, 2 ) );
 
-    geometry.offsets.push( { start: 0, count: indexArray.length, index: 0 } );
+    geometry.offsets.push( { start: 0, count: indices.length, index: 0 } );
 
 	geometry.computeBoundingSphere();
 

+ 35 - 19
examples/js/loaders/ctm/CTMLoader.js

@@ -190,38 +190,54 @@ THREE.CTMLoader.prototype.load = function( url, callback, parameters ) {
 
 THREE.CTMLoader.prototype.createModel = function ( file, callback ) {
 
-	var Model = function ( ) {
+	var Model = function () {
 
 		THREE.BufferGeometry.call( this );
 
 		this.materials = [];
 
-		// init GL buffers
-		var vertexIndexArray = file.body.indices,
-		vertexPositionArray = file.body.vertices,
-		vertexNormalArray = file.body.normals;
+		var indices = file.body.indices,
+		positions = file.body.vertices,
+		normals = file.body.normals;
 
-		var vertexUvArray, vertexColorArray;
+		var uvs, colors;
+
+		var uvMaps = file.body.uvMaps;
+
+		if ( uvMaps !== undefined && uvMaps.length > 0 ) {
+
+			uvs = uvMaps[ 0 ].uv;
+
+		}
+
+		var attrMaps = file.body.attrMaps;
+
+		if ( attrMaps !== undefined && attrMaps.length > 0 && attrMaps[ 0 ].name === 'Color' ) {
+
+			colors = attrMaps[ 0 ].attr;
 
-		if ( file.body.uvMaps !== undefined && file.body.uvMaps.length > 0 ) {
-			vertexUvArray = file.body.uvMaps[ 0 ].uv;
 		}
 
-		if ( file.body.attrMaps !== undefined && file.body.attrMaps.length > 0 && file.body.attrMaps[ 0 ].name === "Color" ) {
-			vertexColorArray = file.body.attrMaps[ 0 ].attr;
+		this.addAttribute( 'index', new THREE.BufferAttribute( indices, 1 ) );
+		this.addAttribute( 'position', new THREE.BufferAttribute( positions, 3 ) );
+
+		if ( normals !== undefined ) {
+
+			this.addAttribute( 'normal', new THREE.BufferAttribute( normals, 3 ) );
+
 		}
 
-		this.addAttribute( 'index', vertexIndexArray, 1 );
-		this.addAttribute( 'position', vertexPositionArray, 3 );
+		if ( uvs !== undefined ) {
+
+			this.addAttribute( 'uv', new THREE.BufferAttribute( uvs, 2 ) );
 
-		if ( vertexNormalArray !== undefined ) 
-			this.addAttribute( 'normal', vertexNormalArray, 3 );
+		}
+
+		if ( colors !== undefined ) {
 
-		if ( vertexUvArray !== undefined ) 
-			this.addAttribute( 'uv', vertexUvArray, 2 );
+			this.addAttribute( 'color', new THREE.BufferAttribute( colors, 4 ) );
 
-		if ( vertexColorArray !== undefined ) 
-			this.addAttribute( 'color', vertexColorArray, 4 );
+		}
 
 	}
 
@@ -238,4 +254,4 @@ THREE.CTMLoader.prototype.createModel = function ( file, callback ) {
 
 	callback( geometry );
 
-};
+};

+ 5 - 55
examples/js/loaders/gltf/glTFLoader.js

@@ -4,8 +4,6 @@
 
 
 THREE.glTFLoader = function (showStatus) {
-	this.useBufferGeometry = (THREE.glTFLoader.useBufferGeometry !== undefined ) ?
-			THREE.glTFLoader.useBufferGeometry : true;
     this.meshesRequested = 0;
     this.meshesLoaded = 0;
     this.pendingMeshes = [];
@@ -86,12 +84,8 @@ THREE.glTFLoader.prototype.load = function( url, callback ) {
 
     var ClassicGeometry = function() {
 
-    	if (theLoader.useBufferGeometry) {
-    		this.geometry = new THREE.BufferGeometry;
-    	}
-    	else {
-    		this.geometry = new THREE.Geometry;
-    	}
+        this.geometry = new THREE.BufferGeometry;
+
         this.totalAttributes = 0;
         this.loadedAttributes = 0;
         this.indicesLoaded = false;
@@ -105,41 +99,6 @@ THREE.glTFLoader.prototype.load = function( url, callback ) {
 
     ClassicGeometry.prototype.constructor = ClassicGeometry;
 
-    ClassicGeometry.prototype.buildArrayGeometry = function() {
-
-    	// Build indexed mesh
-        var geometry = this.geometry;
-        var normals = geometry.normals;
-        var indexArray = this.indexArray;
-        var uvs = this.uvs;
-        var a, b, c;
-        var i, l;
-        var faceNormals = null;
-        var faceTexcoords = null;
-        
-        for(i = 0, l = this.indexArray.length; i < l; i += 3) {
-            a = indexArray[i];
-            b = indexArray[i+1];
-            c = indexArray[i+2];
-            if(normals) {
-                faceNormals = [normals[a], normals[b], normals[c]];
-            }
-            geometry.faces.push( new THREE.Face3( a, b, c, faceNormals, null, null ) );
-            if(uvs) {
-                geometry.faceVertexUvs[0].push([ uvs[a], uvs[b], uvs[c] ]);
-            }
-        }
-
-        // Allow Three.js to calculate some values for us
-        geometry.computeBoundingBox();
-        geometry.computeBoundingSphere();
-        geometry.computeFaceNormals();
-        if(!normals) {
-            geometry.computeVertexNormals();
-        }
-
-    }
-
     ClassicGeometry.prototype.buildBufferGeometry = function() {
         // Build indexed mesh
         var geometry = this.geometry;
@@ -162,12 +121,7 @@ THREE.glTFLoader.prototype.load = function( url, callback ) {
     ClassicGeometry.prototype.checkFinished = function() {
         if(this.indexArray && this.loadedAttributes === this.totalAttributes) {
         	
-        	if (theLoader.useBufferGeometry) {
-        		this.buildBufferGeometry();
-        	}
-        	else {
-        		this.buildArrayGeometry();
-        	}
+        	this.buildBufferGeometry();
         	
             this.finished = true;
 
@@ -314,12 +268,8 @@ THREE.glTFLoader.prototype.load = function( url, callback ) {
     }
     
     VertexAttributeDelegate.prototype.resourceAvailable = function(glResource, ctx) {
-    	if (theLoader.useBufferGeometry) {
-    		this.bufferResourceAvailable(glResource, ctx);
-    	}
-    	else {
-    		this.arrayResourceAvailable(glResource, ctx);
-    	}
+
+    	this.bufferResourceAvailable(glResource, ctx);
     	
         var geom = ctx.geometry;
         geom.loadedAttributes++;

+ 408 - 78
examples/js/math/Lut.js

@@ -4,51 +4,53 @@
 
 THREE.Lut = function ( colormap, numberofcolors ) {
 
-  this.lut = new Array();
-  this.map = THREE.ColorMapKeywords[ colormap ];
-  this.n = numberofcolors;
-  
-  var step = 1. / this.n;
-  
-  for ( var i = 0; i <= 1; i+=step ) {
-  
-    for ( var j = 0; j < this.map.length - 1; j++ ) {
-    
-      if ( i >= this.map[ j ][ 0 ] && i < this.map[ j+1 ][ 0 ] ) {
-      
-        var min = this.map[ j ][ 0 ];
-        var max = this.map[ j+1 ][ 0 ];	
-        var color = new THREE.Color( 0xffffff ); 
-        var minColor = new THREE.Color( 0xffffff ).setHex( this.map[ j ][ 1 ] );
-        var maxColor = new THREE.Color( 0xffffff ).setHex( this.map[ j+1 ][ 1 ] );	
-        			
-        color = minColor.lerp( maxColor, ( i - min ) / ( max - min ) );
-        
-        this.lut.push(color);
-        
-      }
-      
-    }
-    
-  }
-  
-  return this.set( this );
-  
+	this.lut = new Array();
+	this.map = THREE.ColorMapKeywords[ colormap ];
+	this.n = numberofcolors;
+	this.mapname = colormap;
+
+	var step = 1.0 / this.n;
+
+	for ( var i = 0; i <= 1; i += step ) {
+
+		for ( var j = 0; j < this.map.length - 1; j ++ ) {
+
+			if ( i >= this.map[ j ][ 0 ] && i < this.map[ j + 1 ][ 0 ] ) {
+
+				var min = this.map[ j ][ 0 ];
+				var max = this.map[ j + 1 ][ 0 ];
+
+				var color = new THREE.Color( 0xffffff );
+				var minColor = new THREE.Color( 0xffffff ).setHex( this.map[ j ][ 1 ] );
+				var maxColor = new THREE.Color( 0xffffff ).setHex( this.map[ j + 1 ][ 1 ] );
+
+				color = minColor.lerp( maxColor, ( i - min ) / ( max - min ) );
+
+				this.lut.push(color);
+
+			}
+
+		}
+
+	}
+
+	return this.set( this );
+
 };
 
 THREE.Lut.prototype = {
 
 	constructor: THREE.Lut,
-  
-        lut: [], map: [], mapname: 'rainbow' , n: 256, minV: 0, maxV: 1,
-  
+
+	lut: [], map: [], mapname: 'rainbow' , n: 256, minV: 0, maxV: 1, legend: null,
+
 	set: function ( value ) {
 
 		if ( value instanceof THREE.Lut ) {
 
 			this.copy( value );
 
-		} 
+		}
 
 		return this;
 
@@ -57,7 +59,7 @@ THREE.Lut.prototype = {
 	setMin: function ( min ) {
 
 		this.minV = min;
-		
+
 		return this;
 
 	},
@@ -65,77 +67,405 @@ THREE.Lut.prototype = {
 	setMax: function ( max ) {
 
 		this.maxV = max;
-		
+
 		return this;
 
 	},
-	
+
 	changeNumberOfColors: function ( numberofcolors ) {
 
 		this.n = numberofcolors;
-		
+
 		return new THREE.Lut( this.mapname, this.n );
 
 	},
-	
+
 	changeColorMap: function ( colormap ) {
-    
-                this.mapname = colormap;
-		
+
+		this.mapname = colormap;
+
 		return new THREE.Lut( this.mapname, this.n );
 
 	},
-  
-        copy: function ( lut ) {
+
+	copy: function ( lut ) {
 
 		this.lut = lut.lut;
 		this.mapname = lut.mapname;
 		this.map = lut.map;
 		this.n = lut.n;
-                this.minV = lut.minV;
-                this.maxV = lut.maxV;
+		this.minV = lut.minV;
+		this.maxV = lut.maxV;
 
 		return this;
 
 	},
-  
+
 	getColor: function ( alpha ) {
-                
-                
-                if ( alpha <= this.minV ) {
-                
-                            alpha = this.minV;
-                
-                }
-                
-                else if ( alpha >= this.maxV ) {
-                
-                            alpha = this.maxV;               
-                
-                }
-                
-                
-                alpha = ( alpha - this.minV ) / ( this.maxV - this.minV );
-                
-                var colorPosition = Math.round ( alpha * this.n );
-                colorPosition == this.n ? colorPosition -= 1 : colorPosition;
-		
-                return this.lut[ colorPosition ];
-		
+
+		if ( alpha <= this.minV ) {
+
+			alpha = this.minV;
+
+		} else if ( alpha >= this.maxV ) {
+
+			alpha = this.maxV;
+
+		}
+
+		alpha = ( alpha - this.minV ) / ( this.maxV - this.minV );
+
+		var colorPosition = Math.round ( alpha * this.n );
+		colorPosition == this.n ? colorPosition -= 1 : colorPosition;
+
+		return this.lut[ colorPosition ];
+
+	},
+
+	addColorMap: function ( colormapName, arrayOfColors ) {
+
+		THREE.ColorMapKeywords[ colormapName ] = arrayOfColors;
+
+	},
+
+	setLegendOn: function ( parameters ) {
+
+		if ( parameters === undefined ) { parameters = {}; }
+
+		this.legend = {};
+
+		this.legend.layout = parameters.hasOwnProperty( 'layout' ) ? parameters[ 'layout' ] : 'vertical';
+
+		this.legend.position = parameters.hasOwnProperty( 'position' ) ? parameters[ 'position' ] : { 'x': 21.5, 'y': 8, 'z': 5 };
+
+		this.legend.dimensions = parameters.hasOwnProperty( 'dimensions' ) ? parameters[ 'dimensions' ] : { 'width': 0.5, 'height': 3 };
+
+		this.legend.canvas = document.createElement( 'canvas' );
+
+		this.legend.canvas.setAttribute( 'id', 'legend' );
+		this.legend.canvas.setAttribute( 'hidden', true );
+
+		document.body.appendChild( this.legend.canvas );
+
+		this.legend.ctx = this.legend.canvas.getContext( '2d' );
+
+		this.legend.canvas.setAttribute( 'width',  1 );
+		this.legend.canvas.setAttribute( 'height', this.n );
+
+		this.legend.texture = new THREE.Texture( this.legend.canvas );
+
+		imageData = this.legend.ctx.getImageData( 0, 0, 1, this.n );
+
+		data = imageData.data;
+		len = data.length;
+
+		this.map = THREE.ColorMapKeywords[ this.mapname ];
+
+		var k = 0;
+
+		var step = 1.0 / this.n;
+
+		for ( var i = 1; i >= 0; i-=step ) {
+
+			for ( var j = this.map.length - 1; j >= 0; j-- ) {
+
+				if ( i < this.map[ j ][ 0 ] && i >= this.map[ j - 1 ][ 0 ]  ) {
+
+					var min = this.map[ j - 1 ][ 0 ];
+					var max = this.map[ j ][ 0 ];
+					var color = new THREE.Color( 0xffffff );
+					var minColor = new THREE.Color( 0xffffff ).setHex( this.map[ j - 1][ 1 ] );
+					var maxColor = new THREE.Color( 0xffffff ).setHex( this.map[ j ][ 1 ] );
+					color = minColor.lerp( maxColor, ( i - min ) / ( max - min ) );
+
+					data[ k * 4     ] = Math.round( color.r * 255 );
+					data[ k * 4 + 1 ] = Math.round( color.g * 255 );
+					data[ k * 4 + 2 ] = Math.round( color.b * 255 );
+					data[ k * 4 + 3 ] = 255;
+
+					k+=1;
+
+				}
+
+			}
+
+		}
+
+		this.legend.ctx.putImageData( imageData, 0, 0 );
+		this.legend.texture.needsUpdate = true;
+
+		this.legend.legendGeometry = new THREE.PlaneGeometry( this.legend.dimensions.width , this.legend.dimensions.height );
+		this.legend.legendMaterial = new THREE.MeshBasicMaterial( { map : this.legend.texture, side : THREE.DoubleSide } );
+
+		this.legend.mesh = new THREE.Mesh( this.legend.legendGeometry, this.legend.legendMaterial );
+
+		if ( this.legend.layout == 'horizontal') {
+
+			this.legend.mesh.rotation.z = - 90 * ( Math.PI / 180 );
+
+		}
+
+		this.legend.mesh.position.copy( this.legend.position );
+
+		return this.legend.mesh;
+
 	},
 
-        addColorMap: function ( colormapName, arrayOfColors ) {
-        
-                THREE.ColorMapKeywords[ colormapName ] = arrayOfColors;
+	setLegendOff: function () {
+
+		this.legend = null;
+
+		return this.legend;
+
+	},
+
+	setLegendLayout: function ( layout ) {
+
+		if ( ! this.legend ) { return false; }
+
+		if ( this.legend.layout == layout ) { return false; }
+
+		if ( layout != 'horizontal' && layout != 'vertical' ) { return false; }
+
+		this.layout = layout;
+
+		if ( layout == 'horizontal' ) {
+
+			this.legend.mesh.rotation.z = 90 * ( Math.PI / 180 );
+
+		}
+
+		if ( layout == 'vertical' ) {
+
+			this.legend.mesh.rotation.z = -90 * ( Math.PI / 180 );
+
+		}
+
+		return this.legend.mesh;
+
+	},
+
+	setLegendPosition: function ( position ) {
+
+		this.legend.position = new THREE.Vector3( position.x, position.y, position.z );
+
+		return this.legend;
+
+	},
+
+	setLegendLabels: function ( parameters, callback ) {
+
+		if ( ! this.legend ) { return false; }
+
+		if ( typeof parameters === 'function') { callback = parameters; }
+
+		if ( parameters === undefined ) { parameters = {}; }
+
+		this.legend.labels = {};
+
+		this.legend.labels.fontsize = parameters.hasOwnProperty( 'fontsize' ) ? parameters[ 'fontsize' ] : 24;
+
+		this.legend.labels.fontface = parameters.hasOwnProperty( 'fontface' ) ? parameters[ 'fontface' ] : 'Arial';
+
+		this.legend.labels.title = parameters.hasOwnProperty( 'title' ) ? parameters[ 'title' ] : '';
+
+		this.legend.labels.um = parameters.hasOwnProperty( 'um' ) ? ' [ '+ parameters[ 'um' ] + ' ]': '';
+
+		this.legend.labels.ticks = parameters.hasOwnProperty( 'ticks' ) ? parameters[ 'ticks' ] : 0;
+
+		this.legend.labels.decimal = parameters.hasOwnProperty( 'decimal' ) ? parameters[ 'decimal' ] : 2;
+
+		this.legend.labels.notation = parameters.hasOwnProperty( 'notation' ) ? parameters[ 'notation' ] : 'standard';
+
+		var backgroundColor = { r: 255, g: 100, b: 100, a: 0.8 };
+		var borderColor =  { r: 255, g: 0, b: 0, a: 1.0 };
+		var borderThickness = 4;
+
+		var canvasTitle = document.createElement( 'canvas' );
+		var contextTitle = canvasTitle.getContext( '2d' );
+
+		contextTitle.font = 'Normal ' + this.legend.labels.fontsize * 1.2 + 'px ' + this.legend.labels.fontface;
+
+		var metrics = contextTitle.measureText( this.legend.labels.title.toString() + this.legend.labels.um.toString() );
+		var textWidth = metrics.width;
+
+		contextTitle.fillStyle   = 'rgba(' + backgroundColor.r + ',' + backgroundColor.g + ',' + backgroundColor.b + ',' + backgroundColor.a + ')';
+
+		contextTitle.strokeStyle = 'rgba(' + borderColor.r + ',' + borderColor.g + ',' + borderColor.b + ',' + borderColor.a + ')';
+
+		contextTitle.lineWidth = borderThickness;
+
+		contextTitle.fillStyle = 'rgba( 0, 0, 0, 1.0 )';
+
+		contextTitle.fillText( this.legend.labels.title.toString() + this.legend.labels.um.toString(), borderThickness, this.legend.labels.fontsize + borderThickness );
+
+		var txtTitle = new THREE.Texture( canvasTitle );
+
+		txtTitle.needsUpdate = true;
+
+		var spriteMaterialTitle = new THREE.SpriteMaterial( { map: txtTitle, useScreenCoordinates: false } );
+
+		var spriteTitle = new THREE.Sprite( spriteMaterialTitle );
+
+		spriteTitle.scale.set( 2, 1, 1.0 );
+
+		if ( this.legend.layout == 'vertical' ) {
+
+			spriteTitle.position.set( this.legend.position.x + this.legend.dimensions.width, this.legend.position.y + ( this.legend.dimensions.height * 0.45 ), this.legend.position.z );
+
+		}
+
+		if ( this.legend.layout == 'horizontal' ) {
+
+			spriteTitle.position.set( this.legend.position.x * 1.015, this.legend.position.y + ( this.legend.dimensions.height * 0.03 ), this.legend.position.z );
+
+		}
+
+		if ( this.legend.labels.ticks > 0 ) {
+
+			var ticks = {};
+			var lines = {};
+
+			if ( this.legend.layout == 'vertical' ) {
+
+				var topPositionY = this.legend.position.y + ( this.legend.dimensions.height * 0.36 );
+				var bottomPositionY = this.legend.position.y - ( this.legend.dimensions.height * 0.61 );
+
+			}
+
+			if ( this.legend.layout == 'horizontal' ) {
+
+				var topPositionX = this.legend.position.x + ( this.legend.dimensions.height * 0.75 );
+				var bottomPositionX = this.legend.position.x - ( this.legend.dimensions.width * 1.2  ) ;
+
+			}
+
+			for ( var i = 0; i < this.legend.labels.ticks; i++ ) {
+
+				var value = ( this.maxV - this.minV ) / ( this.legend.labels.ticks - 1  ) * i ;
+
+				if ( callback ) {
+
+					value = callback ( value );
+
+				}
+
+				else {
+
+					if ( this.legend.labels.notation == 'scientific' ) {
+
+						value = value.toExponential( this.legend.labels.decimal );
+
+					}
+
+					else {
+
+						value = value.toFixed( this.legend.labels.decimal );
+
+					}
+
+				}
+
+				var canvasTick = document.createElement( 'canvas' );
+				var contextTick = canvasTick.getContext( '2d' );
+
+				contextTick.font = 'Normal ' + this.legend.labels.fontsize + 'px ' + this.legend.labels.fontface;
+
+				var metrics = contextTick.measureText( value.toString() );
+				var textWidth = metrics.width;
+
+				contextTick.fillStyle   = 'rgba(' + backgroundColor.r + ',' + backgroundColor.g + ',' + backgroundColor.b + ',' + backgroundColor.a + ')';
+
+				contextTick.strokeStyle = 'rgba(' + borderColor.r + ',' + borderColor.g + ',' + borderColor.b + ',' + borderColor.a + ')';
+
+				contextTick.lineWidth = borderThickness;
+
+				contextTick.fillStyle = 'rgba( 0, 0, 0, 1.0 )';
+
+				contextTick.fillText( value.toString(), borderThickness, this.legend.labels.fontsize + borderThickness );
+
+				var txtTick = new THREE.Texture( canvasTick );
+
+				txtTick.needsUpdate = true;
+
+				var spriteMaterialTick = new THREE.SpriteMaterial( { map: txtTick, useScreenCoordinates: false } );
+
+				var spriteTick = new THREE.Sprite( spriteMaterialTick );
+
+				spriteTick.scale.set( 2, 1, 1.0 );
+
+				if ( this.legend.layout == 'vertical' ) {
+
+					var position = bottomPositionY + ( topPositionY - bottomPositionY ) * ( value / ( this.maxV - this.minV ) );
+
+					spriteTick.position.set( this.legend.position.x + ( this.legend.dimensions.width * 2.7 ), position, this.legend.position.z );
+
+				}
+
+				if ( this.legend.layout == 'horizontal' ) {
+
+					var position = bottomPositionX + ( topPositionX - bottomPositionX ) * ( value / ( this.maxV - this.minV ) );
+
+					if ( this.legend.labels.ticks > 5 ) {
+
+						if ( i % 2 === 0 ) { var offset = 1.7; }
+
+						else { var offset = 2.1; }
+
+					}
+
+					else { var offset = 1.7; }
+
+					spriteTick.position.set( position, this.legend.position.y - this.legend.dimensions.width * offset, this.legend.position.z );
+
+				}
+
+				var material = new THREE.LineBasicMaterial( { color: 0x000000, linewidth: 2 } );
+
+				var geometry = new THREE.Geometry();
+
+
+				if ( this.legend.layout == 'vertical' ) {
+
+					var linePosition = ( this.legend.position.y - ( this.legend.dimensions.height * 0.5 ) + 0.01 ) + ( this.legend.dimensions.height ) * ( value / ( this.maxV - this.minV ) * 0.99 );
+
+					geometry.vertices.push( new THREE.Vector3( this.legend.position.x + this.legend.dimensions.width * 0.55, linePosition , this.legend.position.z  ) );
+
+					geometry.vertices.push( new THREE.Vector3( this.legend.position.x + this.legend.dimensions.width * 0.7, linePosition, this.legend.position.z  ) );
+
+				}
+
+				if ( this.legend.layout == 'horizontal' ) {
+
+					var linePosition = ( this.legend.position.x - ( this.legend.dimensions.height * 0.5 ) + 0.01 ) + ( this.legend.dimensions.height ) * ( value / ( this.maxV - this.minV ) * 0.99 );
+
+					geometry.vertices.push( new THREE.Vector3( linePosition, this.legend.position.y - this.legend.dimensions.width * 0.55, this.legend.position.z  ) );
+
+					geometry.vertices.push( new THREE.Vector3( linePosition, this.legend.position.y - this.legend.dimensions.width * 0.7, this.legend.position.z  ) );
+
+				}
+
+				var line = new THREE.Line( geometry, material );
+
+				lines[ i ] = line;
+				ticks[ i ] = spriteTick;
+
+			}
+
+		}
+
+		return { 'title': spriteTitle,  'ticks': ticks, 'lines': lines };
+
+	}
 
-        },
-	
 };
 
+
 THREE.ColorMapKeywords = {
 
-                "rainbow": [ [ 0.0, '0x0000FF' ], [ 0.2, '0x00FFFF' ], [ 0.5, '0x00FF00' ], [ 0.8, '0xFFFF00'],  [1.0, '0xFF0000' ] ],
-                "cooltowarm": [ [ 0.0, '0x3C4EC2' ], [ 0.2, '0x9BBCFF' ], [ 0.5, '0xDCDCDC' ], [ 0.8, '0xF6A385'],  [1.0, '0xB40426' ] ],
-                "blackbody" : [ [ 0.0, '0x000000' ], [ 0.2, '0x780000' ], [ 0.5, '0xE63200' ], [ 0.8, '0xFFFF00'],  [1.0, '0xFFFFFF' ] ]
+  "rainbow":    [ [ 0.0, '0x0000FF' ], [ 0.2, '0x00FFFF' ], [ 0.5, '0x00FF00' ], [ 0.8, '0xFFFF00'],  [1.0, '0xFF0000' ] ],
+  "cooltowarm": [ [ 0.0, '0x3C4EC2' ], [ 0.2, '0x9BBCFF' ], [ 0.5, '0xDCDCDC' ], [ 0.8, '0xF6A385'],  [1.0, '0xB40426' ] ],
+  "blackbody" : [ [ 0.0, '0x000000' ], [ 0.2, '0x780000' ], [ 0.5, '0xE63200' ], [ 0.8, '0xFFFF00'],  [1.0, '0xFFFFFF' ] ],
+  "grayscale" : [ [ 0.0, '0x000000' ], [ 0.2, '0x404040' ], [ 0.5, '0x7F7F80' ], [ 0.8, '0xBFBFBF'],  [1.0, '0xFFFFFF' ] ]
 
-}
+};

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