Przeglądaj źródła

Merge remote-tracking branch 'mrdoob/dev' into dev

Daniel 9 lat temu
rodzic
commit
e73e2d16a4
61 zmienionych plików z 2651 dodań i 428 usunięć
  1. 164 82
      build/three.js
  2. 39 39
      build/three.min.js
  3. 9 9
      docs/api/math/Box2.html
  4. 14 14
      docs/api/math/Box3.html
  5. 4 4
      docs/api/math/Plane.html
  6. 9 9
      docs/api/math/Ray.html
  7. 1 1
      editor/js/Sidebar.Material.js
  8. 3 10
      editor/js/Sidebar.Project.js
  9. 2 1
      examples/index.html
  10. 674 0
      examples/js/libs/earcut.js
  11. 14 5
      examples/js/renderers/CanvasRenderer.js
  12. 19 19
      examples/js/renderers/Projector.js
  13. 2 2
      examples/js/renderers/SVGRenderer.js
  14. 688 0
      examples/webgl_geometry_text_earcut.html
  15. 0 0
      examples/webgl_geometry_text_pnltri.html
  16. 1 0
      examples/webgl_loader_ctm_materials.html
  17. 6 10
      src/extras/helpers/ArrowHelper.js
  18. 13 7
      src/loaders/JSONLoader.js
  19. 5 2
      src/loaders/Loader.js
  20. 191 0
      src/materials/MeshStandardMaterial.js
  21. 10 2
      src/math/Box2.js
  22. 40 6
      src/math/Box3.js
  23. 1 1
      src/math/Matrix3.js
  24. 18 11
      src/math/Plane.js
  25. 57 31
      src/math/Ray.js
  26. 6 0
      src/math/Sphere.js
  27. 1 5
      src/objects/Line.js
  28. 2 2
      src/objects/Mesh.js
  29. 1 5
      src/objects/Points.js
  30. 78 2
      src/renderers/WebGLRenderer.js
  31. 83 0
      src/renderers/shaders/ShaderChunk/common.glsl
  32. 1 1
      src/renderers/shaders/ShaderChunk/envmap_pars_fragment.glsl
  33. 1 1
      src/renderers/shaders/ShaderChunk/envmap_pars_vertex.glsl
  34. 63 0
      src/renderers/shaders/ShaderChunk/envmap_standard_fragment.glsl
  35. 1 1
      src/renderers/shaders/ShaderChunk/envmap_vertex.glsl
  36. 0 0
      src/renderers/shaders/ShaderChunk/lights_pars.glsl
  37. 0 37
      src/renderers/shaders/ShaderChunk/lights_phong_pars_fragment.glsl
  38. 112 0
      src/renderers/shaders/ShaderChunk/lights_standard_fragment.glsl
  39. 2 2
      src/renderers/shaders/ShaderChunk/logdepthbuf_vertex.glsl
  40. 8 0
      src/renderers/shaders/ShaderChunk/metalnessmap_fragment.glsl
  41. 5 0
      src/renderers/shaders/ShaderChunk/metalnessmap_pars_fragment.glsl
  42. 8 0
      src/renderers/shaders/ShaderChunk/roughnessmap_fragment.glsl
  43. 5 0
      src/renderers/shaders/ShaderChunk/roughnessmap_pars_fragment.glsl
  44. 1 1
      src/renderers/shaders/ShaderChunk/uv_pars_fragment.glsl
  45. 1 1
      src/renderers/shaders/ShaderChunk/uv_pars_vertex.glsl
  46. 1 1
      src/renderers/shaders/ShaderChunk/uv_vertex.glsl
  47. 1 1
      src/renderers/shaders/ShaderChunk/worldpos_vertex.glsl
  48. 167 1
      src/renderers/shaders/ShaderLib.js
  49. 18 0
      src/renderers/shaders/UniformsLib.js
  50. 10 0
      src/renderers/webgl/WebGLProgram.js
  51. 5 0
      src/renderers/webgl/WebGLPrograms.js
  52. 10 10
      test/unit/math/Box2.js
  53. 10 10
      test/unit/math/Box3.js
  54. 11 11
      test/unit/math/Plane.js
  55. 44 46
      test/unit/math/Ray.js
  56. 3 2
      utils/build/build.py
  57. 8 1
      utils/build/includes/common.json
  58. 0 2
      utils/exporters/blender/addons/io_three/constants.py
  59. 0 16
      utils/exporters/blender/addons/io_three/exporter/api/material.py
  60. 0 1
      utils/exporters/blender/addons/io_three/exporter/api/mesh.py
  61. 0 3
      utils/exporters/blender/addons/io_three/exporter/material.py

Plik diff jest za duży
+ 164 - 82
build/three.js


Plik diff jest za duży
+ 39 - 39
build/three.min.js


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

@@ -22,7 +22,7 @@
 		max -- Upper (x, y) boundary of the box.
 		</div>
 		<div>
-		Creates a box bounded by min and max. 
+		Creates a box bounded by min and max.
 		</div>
 
 
@@ -33,12 +33,12 @@
 		<h3>[property:Vector2 min]</h3>
 		<div>
 		Lower (x, y) boundary of this box.
-		</div> 
+		</div>
 
 		<h3>[property:Vector2 max]</h3>
 		<div>
 		Upper (x, y) boundary of this box.
-		</div> 
+		</div>
 
 		<h2>Methods</h2>
 
@@ -70,7 +70,7 @@
 		Clamps *point* within the bounds of this box.
 		</div>
 
-		<h3>[method:Boolean isIntersectionBox]([page:Box2 box])</h3>
+		<h3>[method:Boolean intersectsBox]([page:Box2 box])</h3>
 		<div>
 		box -- Box to check for intersection against.
 		</div>
@@ -99,7 +99,7 @@
 		box -- Box that will be unioned with this box.
 		</div>
 		<div>
-		Unions this box with *box* setting the upper bound of this box to the greater of the 
+		Unions this box with *box* setting the upper bound of this box to the greater of the
 		two boxes' upper bounds and the lower bound of this box to the lesser of the two boxes'
 		lower bounds.
 		</div>
@@ -108,7 +108,7 @@
 		<div>
 		point -- [page:Vector2]<br/>
 		optionalTarget -- [page:Vector2]<br/>
-		
+
 		</div>
 		<div>
 		Returns a point as a proportion of this box's width and height.
@@ -139,7 +139,7 @@
 		</div>
 		<div>
 		Returns true if this box includes the entirety of *box*. If this and *box* overlap exactly,</br>
-		this function also returns true. 
+		this function also returns true.
 		</div>
 
 		<h3>[method:Box2 translate]([page:Vector2 offset]) [page:Box2 this]</h3>
@@ -177,7 +177,7 @@
 		</div>
 		<div>
 		Expands this box equilaterally by *vector*. The width of this box will be
-		expanded by the x component of *vector* in both directions. The height of 
+		expanded by the x component of *vector* in both directions. The height of
 		this box will be expanded by the y component of *vector* in both directions.
 		</div>
 
@@ -222,7 +222,7 @@
 		<h3>[method:Box2 setFromCenterAndSize]([page:Vector2 center], [page:Vector2 size]) [page:Box2 this]</h3>
 		<div>
 		center -- Desired center position of the box. <br />
-		size -- Desired x and y dimensions of the box. 
+		size -- Desired x and y dimensions of the box.
 		</div>
 		<div>
 		Centers this box on *center* and sets this box's width and height to the values specified

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

@@ -22,7 +22,7 @@
 		max -- Upper (x, y, z) boundary of the box.
 		</div>
 		<div>
-		Creates a box bounded by min and max. 
+		Creates a box bounded by min and max.
 		</div>
 
 		<h2>Properties</h2>
@@ -37,7 +37,7 @@
 		<h3>[property:Vector3 max]</h3>
 		<div>
 		Upper (x, y, z) boundary of this box.
-		</div> 
+		</div>
 
 		<h2>Methods</h2>
 
@@ -51,7 +51,7 @@
 		<div>
 		Sets the lower and upper (x, y, z) boundaries of this box.
 		</div>
-		
+
 		<h3>[method:Box3 applyMatrix4]([page:Matrix4 matrix]) [page:Box3 this]</h3>
 		<div>
 		matrix -- The [page:Matrix4] to apply
@@ -69,7 +69,7 @@
 		Clamps *point* within the bounds of this box.
 		</div>
 
-		<h3>[method:Boolean isIntersectionBox]([page:Box3 box])</h3>
+		<h3>[method:Boolean intersectsBox]([page:Box3 box])</h3>
 		<div>
 		box -- Box to check for intersection against.
 		</div>
@@ -84,7 +84,7 @@
 		<div>
 		Sets the upper and lower bounds of this box to include all of the points in *points*.
 		</div>
-		
+
 		<h3>[method:Box3 setFromObject]([page:Object3D object]) [page:Box3 this]</h3>
 		<div>
 		object -- [page:Object3D] to compute the bounding box for.
@@ -93,8 +93,8 @@
 		Computes the world-axis-aligned bounding box of an object (including its children),
 		accounting for both the object's, and childrens', world transforms
 		</div>
-		
-		
+
+
 
 		<h3>[method:Vector3 size]([page:Vector3 optionalTarget])</h3>
 		<div>
@@ -109,7 +109,7 @@
 		box -- Box that will be unioned with this box.
 		</div>
 		<div>
-		Unions this box with *box* setting the upper bound of this box to the greater of the 
+		Unions this box with *box* setting the upper bound of this box to the greater of the
 		two boxes' upper bounds and the lower bound of this box to the lesser of the two boxes'
 		lower bounds.
 		</div>
@@ -139,7 +139,7 @@
 		</div>
 		<div>
 		Returns true if this box includes the entirety of *box*. If this and *box* overlap exactly,</br>
-		this function also returns true. 
+		this function also returns true.
 		</div>
 
 		<h3>[method:Boolean containsPoint]([page:Vector3 point])</h3>
@@ -149,7 +149,7 @@
 		<div>
 		Returns true if the specified point lies within the boundaries of this box.
 		</div>
-		
+
 		<h3>[method:Box3 translate]([page:Vector3 offset]) [page:Box3 this]</h3>
 		<div>
 		offset -- Direction and distance of offset.
@@ -186,7 +186,7 @@
 		<div>
 		Expands the boundaries of this box to include *point*.
 		</div>
-		
+
 		<h3>[method:Box3 expandByScalar]([page:float scalar]) [page:Box3 this]</h3>
 		<div>
 		scalar -- Distance to expand.
@@ -195,14 +195,14 @@
 		Expands each dimension of the box by *scalar*. If negative, the dimensions of the box <br/>
 		will be contracted.
 		</div>
-		
+
 		<h3>[method:Box3 expandByVector]([page:Vector3 vector]) [page:Box3 this]</h3>
 		<div>
 		vector -- Amount to expand this box in each dimension.
 		</div>
 		<div>
 		Expands this box equilaterally by *vector*. The width of this box will be
-		expanded by the x component of *vector* in both directions. The height of 
+		expanded by the x component of *vector* in both directions. The height of
 		this box will be expanded by the y component of *vector* in both directions.
 		The depth of this box will be expanded by the z component of *vector* in
 		both directions.
@@ -249,7 +249,7 @@
 		<h3>[method:Box3 setFromCenterAndSize]([page:Vector3 center], [page:Vector3 size]) [page:Box3 this]</h3>
 		<div>
 		center -- Desired center position of the box. <br />
-		size -- Desired x and y dimensions of the box. 
+		size -- Desired x and y dimensions of the box.
 		</div>
 		<div>
 		Centers this box on *center* and sets this box's width and height to the values specified

+ 4 - 4
docs/api/math/Plane.html

@@ -28,7 +28,7 @@
 		<h3>[property:Vector3 normal]</h3>
 
 		<h3>[property:Float constant]</h3>
-		
+
 		<h2>Methods</h2>
 
 
@@ -62,9 +62,9 @@
 		</div>
 		<div>
 		Apply a Matrix4 to the plane. The second parameter is optional.
-		
+
 		<code>
-		var optionalNormalMatrix = new THREE.Matrix3().getNormalMatrix( matrix ) 
+		var optionalNormalMatrix = new THREE.Matrix3().getNormalMatrix( matrix )
 		</code>
 		</div>
 
@@ -77,7 +77,7 @@
 		Returns a vector in the same direction as the Plane's normal, but the magnitude is passed point's original distance to the plane.
 		</div>
 
-		<h3>[method:Boolean isIntersectionLine]([page:Line3 line])</h3>
+		<h3>[method:Boolean intersectsLine]([page:Line3 line])</h3>
 		<div>
 		line -- [page:Line3]
 		</div>

+ 9 - 9
docs/api/math/Ray.html

@@ -30,12 +30,12 @@
 		<h3>[property:Vector3 origin]</h3>
 		<div>
 		The origin of the [page:Ray].
-		</div> 
+		</div>
 
 		<h3>[property:Vector3 direction]</h3>
 		<div>
 		The direction of the [page:Ray]. This must be normalized (with [page:Vector3].normalize) for the methods to operate properly.
-		</div> 
+		</div>
 
 		<h2>Methods</h2>
 
@@ -77,7 +77,7 @@
 		<div>
 		Copy the properties of the provided [page:Ray], then return this [page:Ray].
 		</div>
-		
+
 		<h3>.distanceSqToSegment([page:Vector3 v0], [page:Vector3 v1], [page:Vector3 optionalPointOnRay] = null, [page:Vector3 optionalPointOnSegment] = null) [page:Float]</h3>
 		<div>
 		v0 -- [page:Vector3] The start of the line segment.
@@ -129,7 +129,7 @@
 		<div>
 		Intersect this [page:Ray] with a [page:Box3], returning the intersection point or *null* if there is no intersection.
 		</div>
-		
+
 		<h3>.intersectPlane([page:Plane plane], [page:Vector3 optionalTarget] = null) [page:Vector3]?</h3>
 		<div>
 		plane -- [page:Plane] The [page:Plane] to intersect with.<br />
@@ -139,7 +139,7 @@
 		Intersect this [page:Ray] with a [page:Plane], returning the intersection point or *null* if there is no intersection.
 		</div>
 		function ( a, b, c, backfaceCulling, optionalTarget )
-		
+
 		<h3>.intersectTriangle([page:Vector3 a], [page:Vector3 b], [page:Vector3 c], [page:Boolean backfaceCulling], [page:Vector3 optionalTarget] = null) [page:Vector3]?</h3>
 		<div>
 		a, b, c -- [page:Vector3] The [page:Vector3] points on the triangle.<br />
@@ -149,8 +149,8 @@
 		<div>
 		Intersect this [page:Ray] with a triangle, returning the intersection point or *null* if there is no intersection.
 		</div>
-		
-		<h3>[method:Boolean isIntersectionBox]([page:Box3 box])</h3>
+
+		<h3>[method:Boolean intersectsBox]([page:Box3 box])</h3>
 		<div>
 		box -- [page:Box3] The [page:Box3] to intersect with.
 		</div>
@@ -158,7 +158,7 @@
 		Return whether or not this [page:Ray] intersects with the [page:Box3].
 		</div>
 
-		<h3>[method:Boolean isIntersectionPlane]([page:Plane plane])</h3>
+		<h3>[method:Boolean intersectsPlane]([page:Plane plane])</h3>
 		<div>
 		plane -- [page:Plane] The [page:Plane] to intersect with.
 		</div>
@@ -166,7 +166,7 @@
 		Return whether or not this [page:Ray] intersects with the [page:Plane].
 		</div>
 
-		<h3>[method:Boolean isIntersectionSphere]([page:Sphere sphere])</h3>
+		<h3>[method:Boolean intersectsSphere]([page:Sphere sphere])</h3>
 		<div>
 		sphere -- [page:Sphere] The [page:Sphere] to intersect with.
 		</div>

+ 1 - 1
editor/js/Sidebar.Material.js

@@ -343,7 +343,7 @@ Sidebar.Material = function ( editor ) {
 	// opacity
 
 	var materialOpacityRow = new UI.Panel();
-	var materialOpacity = new UI.Number().setWidth( '60px' ).setRange( 0, 1 ).onChange( update );
+	var materialOpacity = new UI.Number( 1 ).setWidth( '60px' ).setRange( 0, 1 ).onChange( update );
 
 	materialOpacityRow.add( new UI.Text( 'Opacity' ).setWidth( '90px' ) );
 	materialOpacityRow.add( materialOpacity );

+ 3 - 10
editor/js/Sidebar.Project.js

@@ -45,17 +45,8 @@ Sidebar.Project = function ( editor ) {
 
 		var value = this.getValue();
 
-		if ( value === 'WebGLRenderer' ) {
-
-			rendererPropertiesRow.setDisplay( '' );
-
-		} else {
-
-			rendererPropertiesRow.setDisplay( 'none' );
-
-		}
-
 		config.setKey( 'project/renderer', value );
+
 		updateRenderer();
 
 	} );
@@ -137,6 +128,8 @@ Sidebar.Project = function ( editor ) {
 
 		}
 
+		rendererPropertiesRow.setDisplay( type === 'WebGLRenderer' ? '' : 'none' );
+
 		var renderer = new rendererTypes[ type ]( { antialias: antialias } );
 		if ( shadows && renderer.shadowMap ) renderer.shadowMap.enabled = true;
 		signals.rendererChanged.dispatch( renderer );

+ 2 - 1
examples/index.html

@@ -225,7 +225,8 @@
 				"webgl_geometry_terrain_fog",
 				"webgl_geometry_terrain_raycast",
 				"webgl_geometry_text",
-				"webgl_geometry_text2",
+				"webgl_geometry_text_earcut",
+				"webgl_geometry_text_pnltri",
 				"webgl_gpgpu_birds",
 				"webgl_gpu_particle_system",
 				"webgl_hdr",

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

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

+ 14 - 5
examples/js/renderers/CanvasRenderer.js

@@ -363,7 +363,7 @@ THREE.CanvasRenderer = function ( parameters ) {
 					_v2.positionScreen
 				] );
 
-				if ( _clipBox.isIntersectionBox( _elemBox ) === true ) {
+				if ( _clipBox.intersectsBox( _elemBox ) === true ) {
 
 					renderLine( _v1, _v2, element, material );
 
@@ -395,7 +395,7 @@ THREE.CanvasRenderer = function ( parameters ) {
 					_v3.positionScreen
 				] );
 
-				if ( _clipBox.isIntersectionBox( _elemBox ) === true ) {
+				if ( _clipBox.intersectsBox( _elemBox ) === true ) {
 
 					renderFace3( _v1, _v2, _v3, 0, 1, 2, element, material );
 
@@ -819,14 +819,23 @@ THREE.CanvasRenderer = function ( parameters ) {
 			texture instanceof THREE.DataTexture ) {
 
 			return {
-					canvas: undefined,
-					version: texture.version
-				}
+				canvas: undefined,
+				version: texture.version
+			}
 
 		}
 
 		var image = texture.image;
 
+		if ( image.complete === false ) {
+
+			return {
+				canvas: undefined,
+				version: 0
+			}
+
+		}
+
 		var canvas = document.createElement( 'canvas' );
 		canvas.width = image.width;
 		canvas.height = image.height;

+ 19 - 19
examples/js/renderers/Projector.js

@@ -161,7 +161,7 @@ THREE.Projector = function () {
 
 		var normalMatrix = new THREE.Matrix3();
 
-		var setObject = function ( value ) {
+		function setObject( value ) {
 
 			object = value;
 			material = object.material;
@@ -171,9 +171,9 @@ THREE.Projector = function () {
 			normals.length = 0;
 			uvs.length = 0;
 
-		};
+		}
 
-		var projectVertex = function ( vertex ) {
+		function projectVertex( vertex ) {
 
 			var position = vertex.position;
 			var positionWorld = vertex.positionWorld;
@@ -192,30 +192,30 @@ THREE.Projector = function () {
 					 positionScreen.y >= - 1 && positionScreen.y <= 1 &&
 					 positionScreen.z >= - 1 && positionScreen.z <= 1;
 
-		};
+		}
 
-		var pushVertex = function ( x, y, z ) {
+		function pushVertex( x, y, z ) {
 
 			_vertex = getNextVertexInPool();
 			_vertex.position.set( x, y, z );
 
 			projectVertex( _vertex );
 
-		};
+		}
 
-		var pushNormal = function ( x, y, z ) {
+		function pushNormal( x, y, z ) {
 
 			normals.push( x, y, z );
 
-		};
+		}
 
-		var pushUv = function ( x, y ) {
+		function pushUv( x, y ) {
 
 			uvs.push( x, y );
 
-		};
+		}
 
-		var checkTriangleVisibility = function ( v1, v2, v3 ) {
+		function checkTriangleVisibility( v1, v2, v3 ) {
 
 			if ( v1.visible === true || v2.visible === true || v3.visible === true ) return true;
 
@@ -223,20 +223,20 @@ THREE.Projector = function () {
 			_points3[ 1 ] = v2.positionScreen;
 			_points3[ 2 ] = v3.positionScreen;
 
-			return _clipBox.isIntersectionBox( _boundingBox.setFromPoints( _points3 ) );
+			return _clipBox.intersectsBox( _boundingBox.setFromPoints( _points3 ) );
 
-		};
+		}
 
-		var checkBackfaceCulling = function ( v1, v2, v3 ) {
+		function checkBackfaceCulling( v1, v2, v3 ) {
 
 			return ( ( v3.positionScreen.x - v1.positionScreen.x ) *
 				    ( v2.positionScreen.y - v1.positionScreen.y ) -
 				    ( v3.positionScreen.y - v1.positionScreen.y ) *
 				    ( v2.positionScreen.x - v1.positionScreen.x ) ) < 0;
 
-		};
+		}
 
-		var pushLine = function ( a, b ) {
+		function pushLine( a, b ) {
 
 			var v1 = _vertexPool[ a ];
 			var v2 = _vertexPool[ b ];
@@ -253,9 +253,9 @@ THREE.Projector = function () {
 
 			_renderData.elements.push( _line );
 
-		};
+		}
 
-		var pushTriangle = function ( a, b, c ) {
+		function pushTriangle( a, b, c ) {
 
 			var v1 = _vertexPool[ a ];
 			var v2 = _vertexPool[ b ];
@@ -298,7 +298,7 @@ THREE.Projector = function () {
 
 			}
 
-		};
+		}
 
 		return {
 			setObject: setObject,

+ 2 - 2
examples/js/renderers/SVGRenderer.js

@@ -170,7 +170,7 @@ THREE.SVGRenderer = function () {
 
 				_elemBox.setFromPoints( [ _v1.positionScreen, _v2.positionScreen ] );
 
-				if ( _clipBox.isIntersectionBox( _elemBox ) === true ) {
+				if ( _clipBox.intersectsBox( _elemBox ) === true ) {
 
 					renderLine( _v1, _v2, element, material );
 
@@ -194,7 +194,7 @@ THREE.SVGRenderer = function () {
 					_v3.positionScreen
 				] );
 
-				if ( _clipBox.isIntersectionBox( _elemBox ) === true ) {
+				if ( _clipBox.intersectsBox( _elemBox ) === true ) {
 
 					renderFace3( _v1, _v2, _v3, element, material );
 

+ 688 - 0
examples/webgl_geometry_text_earcut.html

@@ -0,0 +1,688 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<title>three.js webgl - geometry - text</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 {
+				font-family: Monospace;
+				background-color: #000;
+				color: #fff;
+				margin: 0px;
+				overflow: hidden;
+			}
+			#info {
+				position: absolute;
+				top: 10px;
+				width: 100%;
+				text-align: center;
+				z-index: 100;
+				display:block;
+			}
+			#info a, .button { color: #f00; font-weight: bold; text-decoration: underline; cursor: pointer }
+		</style>
+	</head>
+	<body>
+
+		<div id="info">
+		<a href="http://threejs.org" target="_blank">three.js</a> - procedural 3D text by <a href="http://www.lab4games.net/zz85/blog" target="_blank">zz85</a> &amp; alteredq
+		<br/>built-in shape triangulation has been replaced with <a href="https://github.com/mapbox/earcut">Earcut</a> by <a href="https://github.com/mourner" target="_blank">mourner</a>
+		<br/>type to enter new text, drag to spin the text
+		<br/><span class="button" id="color">change color</span>,
+			<span class="button" id="font">change font</span>,
+			<span class="button" id="weight">change weight</span>,
+			<span class="button" id="bevel">change bevel</span>,
+			<span class="button" id="postprocessing">change postprocessing</span>,
+			<a id="permalink" href="#">permalink</a>
+		</div>
+
+
+		<script src="../build/three.min.js"></script>
+		<script src="js/utils/GeometryUtils.js"></script>
+
+		<script src="js/shaders/ConvolutionShader.js"></script>
+		<script src="js/shaders/CopyShader.js"></script>
+		<script src="js/shaders/FilmShader.js"></script>
+		<script src="js/shaders/FXAAShader.js"></script>
+
+		<script src="js/postprocessing/EffectComposer.js"></script>
+		<script src="js/postprocessing/RenderPass.js"></script>
+		<script src="js/postprocessing/ShaderPass.js"></script>
+		<script src="js/postprocessing/MaskPass.js"></script>
+		<script src="js/postprocessing/BloomPass.js"></script>
+		<script src="js/postprocessing/FilmPass.js"></script>
+
+		<script src="js/Detector.js"></script>
+		<script src="js/libs/stats.min.js"></script>
+
+		<script src="js/geometries/TextGeometry.js"></script>
+		<script src="js/utils/FontUtils.js"></script>
+
+		<script src="fonts/gentilis_bold.typeface.js"></script>
+		<script src="fonts/gentilis_regular.typeface.js"></script>
+		<script src="fonts/optimer_bold.typeface.js"></script>
+		<script src="fonts/optimer_regular.typeface.js"></script>
+		<script src="fonts/helvetiker_bold.typeface.js"></script>
+		<script src="fonts/helvetiker_regular.typeface.js"></script>
+		<script src="fonts/droid/droid_sans_regular.typeface.js"></script>
+		<script src="fonts/droid/droid_sans_bold.typeface.js"></script>
+		<script src="fonts/droid/droid_serif_regular.typeface.js"></script>
+		<script src="fonts/droid/droid_serif_bold.typeface.js"></script>
+
+		<!-- replace built-in triangulation with Earcut -->
+		<script src="js/libs/earcut.js"></script>
+		<script>
+			function addContour( vertices, contour ) {
+			    for ( var i = 0; i < contour.length; i++ ) {
+			        vertices.push( contour[i].x );
+			        vertices.push( contour[i].y );
+			    }
+			}
+
+			THREE.Shape.Utils.triangulateShape = function ( contour, holes ) {
+			    var vertices = [];
+
+			    addContour( vertices, contour );
+
+			    var holeIndices = [];
+			    var holeIndex = contour.length;
+
+			    for ( i = 0; i < holes.length; i++ ) {
+			        holeIndices.push( holeIndex );
+			        holeIndex += holes[i].length;
+			        addContour( vertices, holes[i] );
+			    }
+
+			    var result = earcut( vertices, holeIndices, 2 );
+
+			    var grouped = [];
+			    for ( var i = 0; i < result.length; i += 3 ) {
+			        grouped.push( result.slice( i, i + 3 ) );
+			    }
+			    return grouped;
+			};	
+		</script>
+
+
+		<script>
+
+			if ( ! Detector.webgl ) Detector.addGetWebGLMessage();
+
+			var container, stats, permalink, hex, color;
+
+			var camera, cameraTarget, scene, renderer;
+
+			var composer;
+			var effectFXAA;
+
+			var group, textMesh1, textMesh2, textGeo, material;
+
+			var firstLetter = true;
+
+			var text = "Earcut",
+
+				height = 20,
+				size = 70,
+				hover = 30,
+
+				curveSegments = 4,
+
+				bevelThickness = 2,
+				bevelSize = 1.5,
+				bevelSegments = 3,
+				bevelEnabled = true,
+
+				font = "optimer", // helvetiker, optimer, gentilis, droid sans, droid serif
+				weight = "bold", // normal bold
+				style = "normal"; // normal italic
+
+			var mirror = true;
+
+			var fontMap = {
+
+				"helvetiker": 0,
+				"optimer": 1,
+				"gentilis": 2,
+				"droid sans": 3,
+				"droid serif": 4
+
+			};
+
+			var weightMap = {
+
+				"normal": 0,
+				"bold": 1
+
+			};
+
+			var reverseFontMap = {};
+			var reverseWeightMap = {};
+
+			for ( var i in fontMap ) reverseFontMap[ fontMap[i] ] = i;
+			for ( var i in weightMap ) reverseWeightMap[ weightMap[i] ] = i;
+
+			var targetRotation = 0;
+			var targetRotationOnMouseDown = 0;
+
+			var mouseX = 0;
+			var mouseXOnMouseDown = 0;
+
+			var windowHalfX = window.innerWidth / 2;
+			var windowHalfY = window.innerHeight / 2;
+
+			var postprocessing = { enabled : false };
+			var glow = 0.9;
+
+			init();
+			animate();
+
+			function capitalize( txt ) {
+
+				return txt.substring( 0, 1 ).toUpperCase() + txt.substring( 1 );
+
+			}
+
+			function decimalToHex( d ) {
+
+				var hex = Number( d ).toString( 16 );
+				hex = "000000".substr( 0, 6 - hex.length ) + hex;
+				return hex.toUpperCase();
+
+			}
+
+			function init() {
+
+				container = document.createElement( 'div' );
+				document.body.appendChild( container );
+
+				permalink = document.getElementById( "permalink" );
+
+				// CAMERA
+
+				camera = new THREE.PerspectiveCamera( 30, window.innerWidth / window.innerHeight, 1, 1500 );
+				camera.position.set( 0, 400, 700 );
+
+				cameraTarget = new THREE.Vector3( 0, 150, 0 );
+
+				// SCENE
+
+				scene = new THREE.Scene();
+				scene.fog = new THREE.Fog( 0x000000, 250, 1400 );
+
+				// LIGHTS
+
+				var dirLight = new THREE.DirectionalLight( 0xffffff, 0.125 );
+				dirLight.position.set( 0, 0, 1 ).normalize();
+				scene.add( dirLight );
+
+				var pointLight = new THREE.PointLight( 0xffffff, 1.5 );
+				pointLight.position.set( 0, 100, 90 );
+				scene.add( pointLight );
+
+				//text = capitalize( font ) + " " + capitalize( weight );
+				//text = "abcdefghijklmnopqrstuvwxyz0123456789";
+				//text = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
+
+				// Get text from hash
+
+				var hash = document.location.hash.substr( 1 );
+
+				if ( hash.length !== 0 ) {
+
+					var colorhash  = hash.substring( 0, 6 );
+					var fonthash   = hash.substring( 6, 7 );
+					var weighthash = hash.substring( 7, 8 );
+					var pphash 	   = hash.substring( 8, 9 );
+					var bevelhash  = hash.substring( 9, 10 );
+					var texthash   = hash.substring( 10 );
+
+					hex = colorhash;
+					pointLight.color.setHex( parseInt( colorhash, 16 ) );
+
+					font = reverseFontMap[ parseInt( fonthash ) ];
+					weight = reverseWeightMap[ parseInt( weighthash ) ];
+
+					postprocessing.enabled = parseInt( pphash );
+					bevelEnabled = parseInt( bevelhash );
+
+					text = decodeURI( texthash );
+
+					updatePermalink();
+
+				} else {
+
+					pointLight.color.setHSL( Math.random(), 1, 0.5 );
+					hex = decimalToHex( pointLight.color.getHex() );
+
+				}
+
+				material = new THREE.MeshFaceMaterial( [
+					new THREE.MeshPhongMaterial( { color: 0xffffff, shading: THREE.FlatShading } ), // front
+					new THREE.MeshPhongMaterial( { color: 0xffffff, shading: THREE.SmoothShading } ) // side
+				] );
+
+				group = new THREE.Group();
+				group.position.y = 100;
+
+				scene.add( group );
+
+				createText();
+
+				var plane = new THREE.Mesh(
+					new THREE.PlaneBufferGeometry( 10000, 10000 ),
+					new THREE.MeshBasicMaterial( { color: 0xffffff, opacity: 0.5, transparent: true } )
+				);
+				plane.position.y = 100;
+				plane.rotation.x = - Math.PI / 2;
+				scene.add( plane );
+
+				// RENDERER
+
+				renderer = new THREE.WebGLRenderer( { antialias: true } );
+				renderer.setClearColor( scene.fog.color );
+				renderer.setPixelRatio( window.devicePixelRatio );
+				renderer.setSize( window.innerWidth, window.innerHeight );
+				container.appendChild( renderer.domElement );
+
+				// STATS
+
+				stats = new Stats();
+				stats.domElement.style.position = 'absolute';
+				stats.domElement.style.top = '0px';
+				//container.appendChild( stats.domElement );
+
+				// EVENTS
+
+				document.addEventListener( 'mousedown', onDocumentMouseDown, false );
+				document.addEventListener( 'touchstart', onDocumentTouchStart, false );
+				document.addEventListener( 'touchmove', onDocumentTouchMove, false );
+				document.addEventListener( 'keypress', onDocumentKeyPress, false );
+				document.addEventListener( 'keydown', onDocumentKeyDown, false );
+
+				document.getElementById( "color" ).addEventListener( 'click', function() {
+
+					pointLight.color.setHSL( Math.random(), 1, 0.5 );
+					hex = decimalToHex( pointLight.color.getHex() );
+
+					updatePermalink();
+
+				}, false );
+
+				document.getElementById( "font" ).addEventListener( 'click', function() {
+
+					if ( font == "helvetiker" ) {
+
+						font = "optimer";
+
+					} else if ( font == "optimer" ) {
+
+						font = "gentilis";
+
+					} else if ( font == "gentilis" ) {
+
+						font = "droid sans";
+
+					} else if ( font == "droid sans" ) {
+
+						font = "droid serif";
+
+					} else {
+
+						font = "helvetiker";
+
+					}
+
+					refreshText();
+
+				}, false );
+
+				document.getElementById( "weight" ).addEventListener( 'click', function() {
+
+					if ( weight == "bold" ) {
+
+						weight = "normal";
+
+					} else {
+
+						weight = "bold";
+
+					}
+
+					refreshText();
+
+				}, false );
+
+				document.getElementById( "bevel" ).addEventListener( 'click', function() {
+
+					bevelEnabled = !bevelEnabled;
+
+					refreshText();
+
+				}, false );
+
+				document.getElementById( "postprocessing" ).addEventListener( 'click', function() {
+
+					postprocessing.enabled = !postprocessing.enabled;
+					updatePermalink();
+
+				}, false );
+
+
+				// POSTPROCESSING
+
+				renderer.autoClear = false;
+
+				var renderModel = new THREE.RenderPass( scene, camera );
+				var effectBloom = new THREE.BloomPass( 0.25 );
+				var effectFilm = new THREE.FilmPass( 0.5, 0.125, 2048, false );
+
+				effectFXAA = new THREE.ShaderPass( THREE.FXAAShader );
+
+				var width = window.innerWidth || 2;
+				var height = window.innerHeight || 2;
+
+				effectFXAA.uniforms[ 'resolution' ].value.set( 1 / width, 1 / height );
+
+				effectFilm.renderToScreen = true;
+
+				composer = new THREE.EffectComposer( renderer );
+
+				composer.addPass( renderModel );
+				composer.addPass( effectFXAA );
+				composer.addPass( effectBloom );
+				composer.addPass( effectFilm );
+
+				//
+
+				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 );
+
+				composer.reset();
+
+				effectFXAA.uniforms[ 'resolution' ].value.set( 1 / window.innerWidth, 1 / window.innerHeight );
+
+			}
+
+			//
+
+			function boolToNum( b ) {
+
+				return b ? 1 : 0;
+
+			}
+
+			function updatePermalink() {
+
+				var link = hex + fontMap[ font ] + weightMap[ weight ] + boolToNum( postprocessing.enabled ) + boolToNum( bevelEnabled ) + "#" + encodeURI( text );
+
+				permalink.href = "#" + link;
+				window.location.hash = link;
+
+			}
+
+			function onDocumentKeyDown( event ) {
+
+				if ( firstLetter ) {
+
+					firstLetter = false;
+					text = "";
+
+				}
+
+				var keyCode = event.keyCode;
+
+				// backspace
+
+				if ( keyCode == 8 ) {
+
+					event.preventDefault();
+
+					text = text.substring( 0, text.length - 1 );
+					refreshText();
+
+					return false;
+
+				}
+
+			}
+
+			function onDocumentKeyPress( event ) {
+
+				var keyCode = event.which;
+
+				// backspace
+
+				if ( keyCode == 8 ) {
+
+					event.preventDefault();
+
+				} else {
+
+					var ch = String.fromCharCode( keyCode );
+					text += ch;
+
+					refreshText();
+
+				}
+
+			}
+
+			function createText() {
+
+				textGeo = new THREE.TextGeometry( text, {
+
+					size: size,
+					height: height,
+					curveSegments: curveSegments,
+
+					font: font,
+					weight: weight,
+					style: style,
+
+					bevelThickness: bevelThickness,
+					bevelSize: bevelSize,
+					bevelEnabled: bevelEnabled,
+
+					material: 0,
+					extrudeMaterial: 1
+
+				});
+
+				textGeo.computeBoundingBox();
+				textGeo.computeVertexNormals();
+
+				// "fix" side normals by removing z-component of normals for side faces
+				// (this doesn't work well for beveled geometry as then we lose nice curvature around z-axis)
+
+				if ( ! bevelEnabled ) {
+
+					var triangleAreaHeuristics = 0.1 * ( height * size );
+
+					for ( var i = 0; i < textGeo.faces.length; i ++ ) {
+
+						var face = textGeo.faces[ i ];
+
+						if ( face.materialIndex == 1 ) {
+
+							for ( var j = 0; j < face.vertexNormals.length; j ++ ) {
+
+								face.vertexNormals[ j ].z = 0;
+								face.vertexNormals[ j ].normalize();
+
+							}
+
+							var va = textGeo.vertices[ face.a ];
+							var vb = textGeo.vertices[ face.b ];
+							var vc = textGeo.vertices[ face.c ];
+
+							var s = THREE.GeometryUtils.triangleArea( va, vb, vc );
+
+							if ( s > triangleAreaHeuristics ) {
+
+								for ( var j = 0; j < face.vertexNormals.length; j ++ ) {
+
+									face.vertexNormals[ j ].copy( face.normal );
+
+								}
+
+							}
+
+						}
+
+					}
+
+				}
+
+				var centerOffset = -0.5 * ( textGeo.boundingBox.max.x - textGeo.boundingBox.min.x );
+
+				textMesh1 = new THREE.Mesh( textGeo, material );
+
+				textMesh1.position.x = centerOffset;
+				textMesh1.position.y = hover;
+				textMesh1.position.z = 0;
+
+				textMesh1.rotation.x = 0;
+				textMesh1.rotation.y = Math.PI * 2;
+
+				group.add( textMesh1 );
+
+				if ( mirror ) {
+
+					textMesh2 = new THREE.Mesh( textGeo, material );
+
+					textMesh2.position.x = centerOffset;
+					textMesh2.position.y = -hover;
+					textMesh2.position.z = height;
+
+					textMesh2.rotation.x = Math.PI;
+					textMesh2.rotation.y = Math.PI * 2;
+
+					group.add( textMesh2 );
+
+				}
+
+			}
+
+			function refreshText() {
+
+				updatePermalink();
+
+				group.remove( textMesh1 );
+				if ( mirror ) group.remove( textMesh2 );
+
+				if ( !text ) return;
+
+				createText();
+
+			}
+
+			function onDocumentMouseDown( event ) {
+
+				event.preventDefault();
+
+				document.addEventListener( 'mousemove', onDocumentMouseMove, false );
+				document.addEventListener( 'mouseup', onDocumentMouseUp, false );
+				document.addEventListener( 'mouseout', onDocumentMouseOut, false );
+
+				mouseXOnMouseDown = event.clientX - windowHalfX;
+				targetRotationOnMouseDown = targetRotation;
+
+			}
+
+			function onDocumentMouseMove( event ) {
+
+				mouseX = event.clientX - windowHalfX;
+
+				targetRotation = targetRotationOnMouseDown + ( mouseX - mouseXOnMouseDown ) * 0.02;
+
+			}
+
+			function onDocumentMouseUp( event ) {
+
+				document.removeEventListener( 'mousemove', onDocumentMouseMove, false );
+				document.removeEventListener( 'mouseup', onDocumentMouseUp, false );
+				document.removeEventListener( 'mouseout', onDocumentMouseOut, false );
+
+			}
+
+			function onDocumentMouseOut( event ) {
+
+				document.removeEventListener( 'mousemove', onDocumentMouseMove, false );
+				document.removeEventListener( 'mouseup', onDocumentMouseUp, false );
+				document.removeEventListener( 'mouseout', onDocumentMouseOut, false );
+
+			}
+
+			function onDocumentTouchStart( event ) {
+
+				if ( event.touches.length == 1 ) {
+
+					event.preventDefault();
+
+					mouseXOnMouseDown = event.touches[ 0 ].pageX - windowHalfX;
+					targetRotationOnMouseDown = targetRotation;
+
+				}
+
+			}
+
+			function onDocumentTouchMove( event ) {
+
+				if ( event.touches.length == 1 ) {
+
+					event.preventDefault();
+
+					mouseX = event.touches[ 0 ].pageX - windowHalfX;
+					targetRotation = targetRotationOnMouseDown + ( mouseX - mouseXOnMouseDown ) * 0.05;
+
+				}
+
+			}
+
+			//
+
+			function animate() {
+
+				requestAnimationFrame( animate );
+
+				render();
+				stats.update();
+
+			}
+
+			function render() {
+
+				group.rotation.y += ( targetRotation - group.rotation.y ) * 0.05;
+
+				camera.lookAt( cameraTarget );
+
+				renderer.clear();
+
+				if ( postprocessing.enabled ) {
+
+					composer.render( 0.05 );
+
+				} else {
+
+					renderer.render( scene, camera );
+
+				}
+
+			}
+
+		</script>
+
+	</body>
+</html>

+ 0 - 0
examples/webgl_geometry_text2.html → examples/webgl_geometry_text_pnltri.html


+ 1 - 0
examples/webgl_loader_ctm_materials.html

@@ -248,6 +248,7 @@
 
 						var mm = new THREE.MeshPhongMaterial( { map: m.map } );
 
+						mm.specularMap = m.map;
 						mm.shininess = 30;
 						mm.color.setHex( 0x404040 );
 						mm.metal = true;

+ 6 - 10
src/extras/helpers/ArrowHelper.js

@@ -35,11 +35,9 @@ THREE.ArrowHelper = ( function () {
 
 		this.position.copy( origin );
 		
-		if ( headLength < length ) {
-			this.line = new THREE.Line( lineGeometry, new THREE.LineBasicMaterial( { color: color } ) );
-			this.line.matrixAutoUpdate = false;
-			this.add( this.line );
-		}
+		this.line = new THREE.Line( lineGeometry, new THREE.LineBasicMaterial( { color: color } ) );
+		this.line.matrixAutoUpdate = false;
+		this.add( this.line );
 
 		this.cone = new THREE.Mesh( coneGeometry, new THREE.MeshBasicMaterial( { color: color } ) );
 		this.cone.matrixAutoUpdate = false;
@@ -91,10 +89,8 @@ THREE.ArrowHelper.prototype.setLength = function ( length, headLength, headWidth
 	if ( headLength === undefined ) headLength = 0.2 * length;
 	if ( headWidth === undefined ) headWidth = 0.2 * headLength;
 
-	if ( headLength < length ){
-		this.line.scale.set( 1, length - headLength, 1 );
-		this.line.updateMatrix();
-	}
+	this.line.scale.set( 1, Math.max( 0, length - headLength ), 1 );
+	this.line.updateMatrix();
 
 	this.cone.scale.set( headWidth, headLength, headWidth );
 	this.cone.position.y = length;
@@ -104,7 +100,7 @@ THREE.ArrowHelper.prototype.setLength = function ( length, headLength, headWidth
 
 THREE.ArrowHelper.prototype.setColor = function ( color ) {
 
-	if ( this.line !== undefined ) this.line.material.color.set( color );
+	this.line.material.color.set( color );
 	this.cone.material.color.set( color );
 
 };

+ 13 - 7
src/loaders/JSONLoader.js

@@ -53,17 +53,23 @@ THREE.JSONLoader.prototype = {
 
 			if ( metadata !== undefined ) {
 
-				if ( metadata.type === 'object' ) {
+				var type = metadata.type;
 
-					console.error( 'THREE.JSONLoader: ' + url + ' should be loaded with THREE.ObjectLoader instead.' );
-					return;
+				if ( type !== undefined ) {
 
-				}
+					if ( type.toLowerCase() === 'object' ) {
+
+						console.error( 'THREE.JSONLoader: ' + url + ' should be loaded with THREE.ObjectLoader instead.' );
+						return;
+
+					}
+
+					if ( type.toLowerCase() === 'scene' ) {
 
-				if ( metadata.type === 'scene' ) {
+						console.error( 'THREE.JSONLoader: ' + url + ' should be loaded with THREE.SceneLoader instead.' );
+						return;
 
-					console.error( 'THREE.JSONLoader: ' + url + ' should be loaded with THREE.SceneLoader instead.' );
-					return;
+					}
 
 				}
 

+ 5 - 2
src/loaders/Loader.js

@@ -139,6 +139,9 @@ THREE.Loader.prototype = {
 					case 'blending':
 						json.blending = THREE[ value ];
 						break;
+					case 'colorAmbient':
+						console.warn( 'THREE.Loader.createMaterial: colorAmbient is no longer supported' );
+						break;
 					case 'colorDiffuse':
 						json.color = color.fromArray( value ).getHex();
 						break;
@@ -224,7 +227,7 @@ THREE.Loader.prototype = {
 						json.side = THREE.DoubleSide;
 						break;
 					case 'transparency':
-						console.warn( 'THREE.Loader: transparency has been renamed to opacity' );
+						console.warn( 'THREE.Loader.createMaterial: transparency has been renamed to opacity' );
 						json.opacity = value;
 						break;
 					case 'opacity':
@@ -241,7 +244,7 @@ THREE.Loader.prototype = {
 						if ( value === 'face' ) json.vertexColors = THREE.FaceColors;
 						break;
 					default:
-						console.error( 'Loader.createMaterial: Unsupported', name, value );
+						console.error( 'THREE.Loader.createMaterial: Unsupported', name, value );
 						break;
 				}
 

+ 191 - 0
src/materials/MeshStandardMaterial.js

@@ -0,0 +1,191 @@
+/**
+ * @author WestLangley / http://github.com/WestLangley
+ *
+ * parameters = {
+ *  color: <hex>,
+ *  roughness: <float>,
+ *  reflectivity: <float>,
+ *  metalness: <float>,
+
+ *  emissive: <hex>,
+ *  opacity: <float>,
+ *
+ *  map: new THREE.Texture( <Image> ),
+ *
+ *  lightMap: new THREE.Texture( <Image> ),
+ *  lightMapIntensity: <float>
+ *
+ *  aoMap: new THREE.Texture( <Image> ),
+ *  aoMapIntensity: <float>
+ *
+ *  emissiveMap: new THREE.Texture( <Image> ),
+ *
+ *  bumpMap: new THREE.Texture( <Image> ),
+ *  bumpScale: <float>,
+ *
+ *  normalMap: new THREE.Texture( <Image> ),
+ *  normalScale: <Vector2>,
+ *
+ *  displacementMap: new THREE.Texture( <Image> ),
+ *  displacementScale: <float>,
+ *  displacementBias: <float>,
+ *
+ *  roughnessMap: new THREE.Texture( <Image> ),
+ *
+ *  reflectivityMap: new THREE.Texture( <Image> ),
+ *
+ *  metalnessMap: new THREE.Texture( <Image> ),
+ *
+ *  alphaMap: new THREE.Texture( <Image> ),
+ *
+ *  envMap: new THREE.CubeTexture( [posx, negx, posy, negy, posz, negz] ),
+ *  refractionRatio: <float>,
+ *
+ *  shading: THREE.SmoothShading,
+ *  blending: THREE.NormalBlending,
+ *  depthTest: <bool>,
+ *  depthWrite: <bool>,
+ *
+ *  wireframe: <boolean>,
+ *  wireframeLinewidth: <float>,
+ *
+ *  vertexColors: THREE.NoColors / THREE.VertexColors / THREE.FaceColors,
+ *
+ *  skinning: <bool>,
+ *  morphTargets: <bool>,
+ *  morphNormals: <bool>,
+ *
+ *	fog: <bool>
+ * }
+ */
+
+THREE.MeshStandardMaterial = function ( parameters ) {
+
+	THREE.Material.call( this );
+
+	this.type = 'MeshStandardMaterial';
+
+	this.color = new THREE.Color( 0xffffff ); // diffuse
+	this.roughness = 0.5;
+	this.reflectivity = 1;
+	this.metalness = 0;
+
+	this.emissive = new THREE.Color( 0x000000 );
+
+	this.map = null;
+
+	this.lightMap = null;
+	this.lightMapIntensity = 1.0;
+
+	this.aoMap = null;
+	this.aoMapIntensity = 1.0;
+
+	this.emissiveMap = null;
+
+	this.bumpMap = null;
+	this.bumpScale = 1;
+
+	this.normalMap = null;
+	this.normalScale = new THREE.Vector2( 1, 1 );
+
+	this.displacementMap = null;
+	this.displacementScale = 1;
+	this.displacementBias = 0;
+
+	this.roughnessMap = null;
+
+	this.reflectivityMap = null;
+
+	this.metalnessMap = null;
+
+	this.alphaMap = null;
+
+	this.envMap = null;
+	this.envMapIntensity = 1.0;
+
+	this.refractionRatio = 0.98;
+
+	this.fog = true;
+
+	this.shading = THREE.SmoothShading;
+
+	this.wireframe = false;
+	this.wireframeLinewidth = 1;
+	this.wireframeLinecap = 'round';
+	this.wireframeLinejoin = 'round';
+
+	this.vertexColors = THREE.NoColors;
+
+	this.skinning = false;
+	this.morphTargets = false;
+	this.morphNormals = false;
+
+	this.setValues( parameters );
+
+};
+
+THREE.MeshStandardMaterial.prototype = Object.create( THREE.Material.prototype );
+THREE.MeshStandardMaterial.prototype.constructor = THREE.MeshStandardMaterial;
+
+THREE.MeshStandardMaterial.prototype.copy = function ( source ) {
+
+	THREE.Material.prototype.copy.call( this, source );
+
+	this.color.copy( source.color );
+	this.roughness = source.roughness;
+	this.reflectivity = source.reflectivity;
+	this.metalness = source.metalness;
+
+	this.emissive.copy( source.emissive );
+
+	this.map = source.map;
+
+	this.lightMap = source.lightMap;
+	this.lightMapIntensity = source.lightMapIntensity;
+
+	this.aoMap = source.aoMap;
+	this.aoMapIntensity = source.aoMapIntensity;
+
+	this.emissiveMap = source.emissiveMap;
+
+	this.bumpMap = source.bumpMap;
+	this.bumpScale = source.bumpScale;
+
+	this.normalMap = source.normalMap;
+	this.normalScale.copy( source.normalScale );
+
+	this.displacementMap = source.displacementMap;
+	this.displacementScale = source.displacementScale;
+	this.displacementBias = source.displacementBias;
+
+	this.roughnessMap = source.roughnessMap;
+
+	this.reflectivityMap = source.reflectivityMap;
+
+	this.metalnessMap = source.metalnessMap;
+
+	this.alphaMap = source.alphaMap;
+
+	this.envMap = source.envMap;
+	this.envMapIntensity = source.envMapIntensity;
+
+	this.refractionRatio = source.refractionRatio;
+
+	this.fog = source.fog;
+
+	this.shading = source.shading;
+
+	this.wireframe = source.wireframe;
+	this.wireframeLinewidth = source.wireframeLinewidth;
+	this.wireframeLinecap = source.wireframeLinecap;
+	this.wireframeLinejoin = source.wireframeLinejoin;
+
+	this.vertexColors = source.vertexColors;
+
+	this.skinning = source.skinning;
+	this.morphTargets = source.morphTargets;
+	this.morphNormals = source.morphNormals;
+
+	return this;
+
+};

+ 10 - 2
src/math/Box2.js

@@ -51,7 +51,7 @@ THREE.Box2.prototype = {
 		};
 
 	}(),
-	
+
 	clone: function () {
 
 		return new this.constructor().copy( this );
@@ -165,7 +165,7 @@ THREE.Box2.prototype = {
 
 	},
 
-	isIntersectionBox: function ( box ) {
+	intersectsBox: function ( box ) {
 
 		// using 6 splitting planes to rule out intersections.
 
@@ -180,6 +180,14 @@ THREE.Box2.prototype = {
 
 	},
 
+	isIntersectionBox: function ( box ) {
+
+		console.warn( 'THREE.Box2: .isIntersectionBox() has been renamed to .intersectsBox().' );
+
+		return this.intersectsBox( box );
+
+	},
+
 	clampPoint: function ( point, optionalTarget ) {
 
 		var result = optionalTarget || new THREE.Vector2();

+ 40 - 6
src/math/Box3.js

@@ -191,8 +191,8 @@ THREE.Box3.prototype = {
 	containsPoint: function ( point ) {
 
 		if ( point.x < this.min.x || point.x > this.max.x ||
-		     point.y < this.min.y || point.y > this.max.y ||
-		     point.z < this.min.z || point.z > this.max.z ) {
+				 point.y < this.min.y || point.y > this.max.y ||
+				 point.z < this.min.z || point.z > this.max.z ) {
 
 			return false;
 
@@ -231,13 +231,13 @@ THREE.Box3.prototype = {
 
 	},
 
-	isIntersectionBox: function ( box ) {
+	intersectsBox: function ( box ) {
 
 		// using 6 splitting planes to rule out intersections.
 
 		if ( box.max.x < this.min.x || box.min.x > this.max.x ||
-		     box.max.y < this.min.y || box.min.y > this.max.y ||
-		     box.max.z < this.min.z || box.min.z > this.max.z ) {
+				 box.max.y < this.min.y || box.min.y > this.max.y ||
+				 box.max.z < this.min.z || box.min.z > this.max.z ) {
 
 			return false;
 
@@ -247,6 +247,40 @@ THREE.Box3.prototype = {
 
 	},
 
+	isIntersectionBox: function ( box ) {
+
+		console.warn( 'THREE.Box3: .isIntersectionBox() has been renamed to .intersectsBox().' );
+
+		return this.intersectsBox( box );
+
+	},
+
+	intersectsSphere: ( function () {
+
+		var closestPoint;
+
+		return function intersectsSphere( sphere ) {
+
+			if ( closestPoint === undefined ) closestPoint = new THREE.Vector3();
+
+			// Find the point on the AABB closest to the sphere center.
+			this.clampPoint( sphere.center, closestPoint );
+
+			// If that point is inside the sphere, the AABB and sphere intersect.
+			return closestPoint.distanceToSquared( sphere.center ) <= ( sphere.radius * sphere.radius );
+
+		};
+
+	} )(),
+
+	isIntersectionSphere: function ( sphere ) {
+
+		console.warn( 'THREE.Box3: .isIntersectionSphere() has been renamed to .intersectsSphere().' );
+
+		return this.intersectsSphere( sphere );
+
+	},
+
 	clampPoint: function ( point, optionalTarget ) {
 
 		var result = optionalTarget || new THREE.Vector3();
@@ -325,7 +359,7 @@ THREE.Box3.prototype = {
 			points[ 4 ].set( this.max.x, this.min.y, this.min.z ).applyMatrix4( matrix ); // 100
 			points[ 5 ].set( this.max.x, this.min.y, this.max.z ).applyMatrix4( matrix ); // 101
 			points[ 6 ].set( this.max.x, this.max.y, this.min.z ).applyMatrix4( matrix ); // 110
-			points[ 7 ].set( this.max.x, this.max.y, this.max.z ).applyMatrix4( matrix );  // 111
+			points[ 7 ].set( this.max.x, this.max.y, this.max.z ).applyMatrix4( matrix );	// 111
 
 			this.makeEmpty();
 			this.setFromPoints( points );

+ 1 - 1
src/math/Matrix3.js

@@ -188,7 +188,7 @@ THREE.Matrix3.prototype = {
 
 		if ( det === 0 ) {
 
-			var msg = "Matrix3.getInverse(): can't invert matrix, determinant is 0";
+			var msg = "THREE.Matrix3.getInverse(): can't invert matrix, determinant is 0";
 
 			if ( throwOnInvertible || false ) {
 

+ 18 - 11
src/math/Plane.js

@@ -122,17 +122,6 @@ THREE.Plane.prototype = {
 
 	},
 
-	isIntersectionLine: function ( line ) {
-
-		// Note: this tests if a line intersects the plane, not whether it (or its end-points) are coplanar with it.
-
-		var startSign = this.distanceToPoint( line.start );
-		var endSign = this.distanceToPoint( line.end );
-
-		return ( startSign < 0 && endSign > 0 ) || ( endSign < 0 && startSign > 0 );
-
-	},
-
 	intersectLine: function () {
 
 		var v1 = new THREE.Vector3();
@@ -173,6 +162,24 @@ THREE.Plane.prototype = {
 
 	}(),
 
+	intersectsLine: function ( line ) {
+
+		// Note: this tests if a line intersects the plane, not whether it (or its end-points) are coplanar with it.
+
+		var startSign = this.distanceToPoint( line.start );
+		var endSign = this.distanceToPoint( line.end );
+
+		return ( startSign < 0 && endSign > 0 ) || ( endSign < 0 && startSign > 0 );
+
+	},
+
+	isIntersectionLine: function ( line ) {
+
+		console.warn( 'THREE.Plane: .isIntersectionLine() has been renamed to .intersectsLine().' );
+
+		return this.intersectsLine( line );
+
+	},
 
 	coplanarPoint: function ( optionalTarget ) {
 

+ 57 - 31
src/math/Ray.js

@@ -232,13 +232,6 @@ THREE.Ray.prototype = {
 
 	}(),
 
-
-	isIntersectionSphere: function ( sphere ) {
-
-		return this.distanceToPoint( sphere.center ) <= sphere.radius;
-
-	},
-
 	intersectSphere: function () {
 
 		// from http://www.scratchapixel.com/lessons/3d-basic-lessons/lesson-7-intersecting-simple-shapes/ray-sphere-intersection/
@@ -280,35 +273,24 @@ THREE.Ray.prototype = {
 
 	}(),
 
-	isIntersectionPlane: function ( plane ) {
-
-		// check if the ray lies on the plane first
-
-		var distToPoint = plane.distanceToPoint( this.origin );
-
-		if ( distToPoint === 0 ) {
-
-			return true;
-
-		}
-
-		var denominator = plane.normal.dot( this.direction );
+	intersectsSphere: function ( sphere ) {
 
-		if ( denominator * distToPoint < 0 ) {
+		return this.distanceToPoint( sphere.center ) <= sphere.radius;
 
-			return true;
+	},
 
-		}
+	isIntersectionSphere: function ( sphere ) {
 
-		// ray origin is behind the plane (and is pointing behind it)
+		console.warn( 'THREE.Ray: .isIntersectionSphere() has been renamed to .intersectsSphere().' );
 
-		return false;
+		return this.intersectsSphere( sphere );
 
 	},
 
 	distanceToPlane: function ( plane ) {
 
 		var denominator = plane.normal.dot( this.direction );
+
 		if ( denominator === 0 ) {
 
 			// line is coplanar, return origin
@@ -346,17 +328,41 @@ THREE.Ray.prototype = {
 
 	},
 
-	isIntersectionBox: function () {
 
-		var v = new THREE.Vector3();
 
-		return function ( box ) {
+	intersectsPlane: function ( plane ) {
 
-			return this.intersectBox( box, v ) !== null;
+		// check if the ray lies on the plane first
 
-		};
+		var distToPoint = plane.distanceToPoint( this.origin );
 
-	}(),
+		if ( distToPoint === 0 ) {
+
+			return true;
+
+		}
+
+		var denominator = plane.normal.dot( this.direction );
+
+		if ( denominator * distToPoint < 0 ) {
+
+			return true;
+
+		}
+
+		// ray origin is behind the plane (and is pointing behind it)
+
+		return false;
+
+	},
+
+	isIntersectionPlane: function ( plane ) {
+
+		console.warn( 'THREE.Ray: .isIntersectionPlane() has been renamed to .intersectsPlane().' );
+
+		return this.intersectsPlane( plane );
+
+	},
 
 	intersectBox: function ( box, optionalTarget ) {
 
@@ -429,6 +435,26 @@ THREE.Ray.prototype = {
 
 	},
 
+	intersectsBox: ( function () {
+
+		var v = new THREE.Vector3();
+
+		return function ( box ) {
+
+			return this.intersectBox( box, v ) !== null;
+
+		};
+
+	} )(),
+
+	isIntersectionBox: function ( box ) {
+
+		console.warn( 'THREE.Ray: .isIntersectionBox() has been renamed to .intersectsBox().' );
+
+		return this.intersectsBox( box );
+
+	},
+
 	intersectTriangle: function () {
 
 		// Compute the offset origin, edges, and normal.

+ 6 - 0
src/math/Sphere.js

@@ -98,6 +98,12 @@ THREE.Sphere.prototype = {
 
 	},
 
+	intersectsBox: function ( box ) {
+
+		return box.intersectsSphere( this );
+
+	},
+
 	clampPoint: function ( point, optionalTarget ) {
 
 		var deltaLengthSq = this.center.distanceToSquared( point );

+ 1 - 5
src/objects/Line.js

@@ -43,11 +43,7 @@ THREE.Line.prototype.raycast = ( function () {
 		sphere.copy( geometry.boundingSphere );
 		sphere.applyMatrix4( this.matrixWorld );
 
-		if ( raycaster.ray.isIntersectionSphere( sphere ) === false ) {
-
-			return;
-
-		}
+		if ( raycaster.ray.intersectsSphere( sphere ) === false ) return;
 
 		inverseMatrix.getInverse( this.matrixWorld );
 		ray.copy( raycaster.ray ).applyMatrix4( inverseMatrix );

+ 2 - 2
src/objects/Mesh.js

@@ -169,7 +169,7 @@ THREE.Mesh.prototype.raycast = ( function () {
 		sphere.copy( geometry.boundingSphere );
 		sphere.applyMatrix4( matrixWorld );
 
-		if ( raycaster.ray.isIntersectionSphere( sphere ) === false ) return;
+		if ( raycaster.ray.intersectsSphere( sphere ) === false ) return;
 
 		// Check boundingBox before continuing
 
@@ -178,7 +178,7 @@ THREE.Mesh.prototype.raycast = ( function () {
 
 		if ( geometry.boundingBox !== null ) {
 
-			if ( ray.isIntersectionBox( geometry.boundingBox ) === false ) return;
+			if ( ray.intersectsBox( geometry.boundingBox ) === false ) return;
 
 		}
 

+ 1 - 5
src/objects/Points.js

@@ -32,11 +32,7 @@ THREE.Points.prototype.raycast = ( function () {
 
 		if ( geometry.boundingBox !== null ) {
 
-			if ( ray.isIntersectionBox( geometry.boundingBox ) === false ) {
-
-				return;
-
-			}
+			if ( ray.intersectsBox( geometry.boundingBox ) === false ) return;
 
 		}
 

+ 78 - 2
src/renderers/WebGLRenderer.js

@@ -637,7 +637,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 			_gl.bindBuffer( _gl.ARRAY_BUFFER, buffers.normal );
 
-			if ( material.type !== 'MeshPhongMaterial' && material.shading === THREE.FlatShading ) {
+			if ( material.type !== 'MeshPhongMaterial' && material.type !== 'MeshStandardMaterial' && material.shading === THREE.FlatShading ) {
 
 				for ( var i = 0, l = object.count * 3; i < l; i += 9 ) {
 
@@ -1573,6 +1573,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 			if ( material instanceof THREE.ShaderMaterial ||
 				 material instanceof THREE.MeshPhongMaterial ||
+				 material instanceof THREE.MeshStandardMaterial ||
 				 material.envMap ) {
 
 				if ( p_uniforms.cameraPosition !== undefined ) {
@@ -1587,6 +1588,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 			if ( material instanceof THREE.MeshPhongMaterial ||
 				 material instanceof THREE.MeshLambertMaterial ||
 				 material instanceof THREE.MeshBasicMaterial ||
+				 material instanceof THREE.MeshStandardMaterial ||
 				 material instanceof THREE.ShaderMaterial ||
 				 material.skinning ) {
 
@@ -1665,6 +1667,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 			if ( material instanceof THREE.MeshPhongMaterial ||
 				 material instanceof THREE.MeshLambertMaterial ||
+				 material instanceof THREE.MeshStandardMaterial ||
 				 material.lights ) {
 
 				if ( _lightsNeedUpdate ) {
@@ -1690,7 +1693,8 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 			if ( material instanceof THREE.MeshBasicMaterial ||
 				 material instanceof THREE.MeshLambertMaterial ||
-				 material instanceof THREE.MeshPhongMaterial ) {
+				 material instanceof THREE.MeshPhongMaterial ||
+				 material instanceof THREE.MeshStandardMaterial ) {
 
 				refreshUniformsCommon( m_uniforms, material );
 
@@ -1715,6 +1719,10 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 				refreshUniformsPhong( m_uniforms, material );
 
+			} else if ( material instanceof THREE.MeshStandardMaterial ) {
+
+				refreshUniformsStandard( m_uniforms, material );
+
 			} else if ( material instanceof THREE.MeshDepthMaterial ) {
 
 				m_uniforms.mNear.value = camera.near;
@@ -1932,6 +1940,74 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 	}
 
+	function refreshUniformsStandard ( uniforms, material ) {
+
+		uniforms.roughness.value = material.roughness;
+		//uniforms.reflectivity.value = material.reflectivity; // part of uniforms common
+		uniforms.metalness.value = material.metalness;
+
+		if ( material.roughnessMap ) {
+
+			uniforms.roughnessMap.value = material.roughnessMap;
+
+		}
+
+		if ( material.reflectivityMap ) {
+
+			uniforms.reflectivityMap.value = material.reflectivityMap;
+
+		}
+
+		if ( material.metalnessMap ) {
+
+			uniforms.metalnessMap.value = material.metalnessMap;
+
+		}
+
+		if ( material.lightMap ) {
+
+			uniforms.lightMap.value = material.lightMap;
+			uniforms.lightMapIntensity.value = material.lightMapIntensity;
+
+		}
+
+		if ( material.emissiveMap ) {
+
+			uniforms.emissiveMap.value = material.emissiveMap;
+
+		}
+
+		if ( material.bumpMap ) {
+
+			uniforms.bumpMap.value = material.bumpMap;
+			uniforms.bumpScale.value = material.bumpScale;
+
+		}
+
+		if ( material.normalMap ) {
+
+			uniforms.normalMap.value = material.normalMap;
+			uniforms.normalScale.value.copy( material.normalScale );
+
+		}
+
+		if ( material.displacementMap ) {
+
+			uniforms.displacementMap.value = material.displacementMap;
+			uniforms.displacementScale.value = material.displacementScale;
+			uniforms.displacementBias.value = material.displacementBias;
+
+		}
+
+		if ( material.envMap ) {
+
+			//uniforms.envMap.value = material.envMap; // part of uniforms common
+			uniforms.envMapIntensity.value = material.envMapIntensity;
+
+		}
+
+	}
+
 	function refreshUniformsLights ( uniforms, lights ) {
 
 		uniforms.ambientLightColor.value = lights.ambient;

+ 83 - 0
src/renderers/shaders/ShaderChunk/common.glsl

@@ -99,6 +99,89 @@ vec3 BRDF_BlinnPhong( in vec3 specularColor, in float shininess, in vec3 normal,
 
 }
 
+// Microfacet Models for Refraction through Rough Surfaces - equation (34)
+// http://graphicrants.blogspot.com/2013/08/specular-brdf-reference.html
+// alpha is "roughness squared" in Disney’s reparameterization
+float G_GGX_Smith( in float alpha, in float dotNL, in float dotNV ) {
+
+	// geometry term = G(l)⋅G(v) / 4(n⋅l)(n⋅v)
+
+	float a2 = alpha * alpha;
+
+	float gl = dotNL + pow( a2 + ( 1.0 - a2 ) * dotNL * dotNL, 0.5 );
+
+	float gv = dotNV + pow( a2 + ( 1.0 - a2 ) * dotNV * dotNV, 0.5 );
+
+	return 1.0 / ( gl * gv );
+
+}
+
+// Microfacet Models for Refraction through Rough Surfaces - equation (33)
+// http://graphicrants.blogspot.com/2013/08/specular-brdf-reference.html
+// alpha is "roughness squared" in Disney’s reparameterization
+float D_GGX( in float alpha, in float dotNH ) {
+
+	// factor of 1/PI in distribution term omitted
+
+	float a2 = alpha * alpha;
+
+	float denom = dotNH * dotNH * ( a2 - 1.0 ) + 1.0; // avoid alpha = 0 with dotNH = 1
+
+	return a2 / ( denom * denom );
+
+}
+
+// GGX Distribution, Schlick Fresnel, GGX-Smith Visibility
+vec3 BRDF_GGX( in vec3 specularColor, in float roughness, in vec3 normal, in vec3 lightDir, in vec3 viewDir ) {
+
+	// factor of 1/PI in BRDF omitted
+
+	float alpha = roughness * roughness; // UE4's roughness
+
+	vec3 halfDir = normalize( lightDir + viewDir );
+
+	float dotNL = saturate( dot( normal, lightDir ) );
+	float dotNV = saturate( dot( normal, viewDir ) );
+	float dotNH = saturate( dot( normal, halfDir ) );
+	float dotLH = saturate( dot( lightDir, halfDir ) );
+
+	vec3 F = F_Schlick( specularColor, dotLH );
+
+	float G = G_GGX_Smith( alpha, dotNL, dotNV );
+
+	float D = D_GGX( alpha, dotNH );
+
+	return F * G * D;
+
+}
+
+vec3 BRDF_Lambert( in vec3 diffuseColor ) {
+
+	// factor of 1/PI in BRDF omitted
+
+	return diffuseColor;
+
+}
+
+// ref: https://www.unrealengine.com/blog/physically-based-shading-on-mobile - environmentBRDF for GGX on mobile
+vec3 envBRDFApprox( vec3 specularColor, float roughness, in vec3 normal, in vec3 viewDir  ) {
+
+	float dotNV = saturate( dot( normal, viewDir ) );
+
+	const vec4 c0 = vec4( - 1, - 0.0275, - 0.572, 0.022 );
+
+	const vec4 c1 = vec4( 1, 0.0425, 1.04, - 0.04 );
+
+	vec4 r = roughness * c0 + c1;
+
+	float a004 = min( r.x * r.x, exp2( - 9.28 * dotNV ) ) * r.x + r.y;
+
+	vec2 AB = vec2( -1.04, 1.04 ) * a004 + r.zw;
+
+	return specularColor * AB.x + AB.y;
+
+}
+
 vec3 inputToLinear( in vec3 a ) {
 
 	#ifdef GAMMA_INPUT

+ 1 - 1
src/renderers/shaders/ShaderChunk/envmap_pars_fragment.glsl

@@ -8,7 +8,7 @@
 	#endif
 	uniform float flipEnvMap;
 
-	#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG )
+	#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG ) || defined( STANDARD )
 
 		uniform float refractionRatio;
 

+ 1 - 1
src/renderers/shaders/ShaderChunk/envmap_pars_vertex.glsl

@@ -1,4 +1,4 @@
-#if defined( USE_ENVMAP ) && ! defined( USE_BUMPMAP ) && ! defined( USE_NORMALMAP ) && ! defined( PHONG )
+#if defined( USE_ENVMAP ) && ! defined( USE_BUMPMAP ) && ! defined( USE_NORMALMAP ) && ! defined( PHONG ) && ! defined( STANDARD )
 
 	varying vec3 vReflect;
 

+ 63 - 0
src/renderers/shaders/ShaderChunk/envmap_standard_fragment.glsl

@@ -0,0 +1,63 @@
+#ifdef USE_ENVMAP
+
+	float reflectivityFactor = reflectivity; // fix add map - replace specular strength?
+
+	vec3 cameraToVertex = normalize( vWorldPosition - cameraPosition );
+
+	// Transforming Normal Vectors with the Inverse Transformation
+	vec3 worldNormal = inverseTransformDirection( normal, viewMatrix );
+
+	#ifdef ENVMAP_MODE_REFLECTION
+
+		vec3 reflectVec = reflect( cameraToVertex, worldNormal );
+
+	#else
+
+		vec3 reflectVec = refract( cameraToVertex, worldNormal, refractionRatio );
+
+	#endif
+
+	#ifdef DOUBLE_SIDED
+
+		float flipNormal = ( float( gl_FrontFacing ) * 2.0 - 1.0 );
+
+	#else
+
+		float flipNormal = 1.0;
+
+	#endif
+
+	#ifdef ENVMAP_TYPE_CUBE
+
+		#if defined( TEXTURE_CUBE_LOD_EXT )
+
+			float bias = pow( roughness, 0.5 ) * 7.0; // from bhouston - there are other models for this calculation (roughness; not roughnesFactor)
+
+			vec4 envMapColor = textureCubeLodEXT( envMap, flipNormal * vec3( flipEnvMap * reflectVec.x, reflectVec.yz ), bias );
+
+		#else
+
+			vec4 envMapColor = textureCube( envMap, flipNormal * vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) );
+
+		#endif
+
+	#elif defined( ENVMAP_TYPE_EQUIREC )
+
+		vec2 sampleUV;
+		sampleUV.y = saturate( flipNormal * reflectVec.y * 0.5 + 0.5 );
+		sampleUV.x = atan( flipNormal * reflectVec.z, flipNormal * reflectVec.x ) * RECIPROCAL_PI2 + 0.5;
+		vec4 envMapColor = texture2D( envMap, sampleUV );
+
+	#elif defined( ENVMAP_TYPE_SPHERE )
+
+		vec3 reflectView = flipNormal * normalize((viewMatrix * vec4( reflectVec, 0.0 )).xyz + vec3(0.0,0.0,1.0));
+		vec4 envMapColor = texture2D( envMap, reflectView.xy * 0.5 + 0.5 );
+
+	#endif
+
+	envMapColor.rgb = inputToLinear( envMapColor.rgb );
+
+    outgoingLight += envBRDFApprox( specularColor, roughnessFactor, normal, viewDir  ) * envMapColor.rgb * envMapIntensity * reflectivityFactor;
+
+#endif
+

+ 1 - 1
src/renderers/shaders/ShaderChunk/envmap_vertex.glsl

@@ -1,4 +1,4 @@
-#if defined( USE_ENVMAP ) && ! defined( USE_BUMPMAP ) && ! defined( USE_NORMALMAP ) && ! defined( PHONG )
+#if defined( USE_ENVMAP ) && ! defined( USE_BUMPMAP ) && ! defined( USE_NORMALMAP ) && ! defined( PHONG ) && ! defined( STANDARD )
 
 	vec3 cameraToVertex = normalize( worldPosition.xyz - cameraPosition );
 

+ 0 - 0
src/renderers/shaders/ShaderChunk/lights_lambert_pars_vertex.glsl → src/renderers/shaders/ShaderChunk/lights_pars.glsl


+ 0 - 37
src/renderers/shaders/ShaderChunk/lights_phong_pars_fragment.glsl

@@ -1,42 +1,5 @@
 uniform vec3 ambientLightColor;
 
-#if MAX_DIR_LIGHTS > 0
-
-	uniform vec3 directionalLightColor[ MAX_DIR_LIGHTS ];
-	uniform vec3 directionalLightDirection[ MAX_DIR_LIGHTS ];
-
-#endif
-
-#if MAX_HEMI_LIGHTS > 0
-
-	uniform vec3 hemisphereLightSkyColor[ MAX_HEMI_LIGHTS ];
-	uniform vec3 hemisphereLightGroundColor[ MAX_HEMI_LIGHTS ];
-	uniform vec3 hemisphereLightDirection[ MAX_HEMI_LIGHTS ];
-
-#endif
-
-#if MAX_POINT_LIGHTS > 0
-
-	uniform vec3 pointLightColor[ MAX_POINT_LIGHTS ];
-
-	uniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];
-	uniform float pointLightDistance[ MAX_POINT_LIGHTS ];
-	uniform float pointLightDecay[ MAX_POINT_LIGHTS ];
-
-#endif
-
-#if MAX_SPOT_LIGHTS > 0
-
-	uniform vec3 spotLightColor[ MAX_SPOT_LIGHTS ];
-	uniform vec3 spotLightPosition[ MAX_SPOT_LIGHTS ];
-	uniform vec3 spotLightDirection[ MAX_SPOT_LIGHTS ];
-	uniform float spotLightAngleCos[ MAX_SPOT_LIGHTS ];
-	uniform float spotLightExponent[ MAX_SPOT_LIGHTS ];
-	uniform float spotLightDistance[ MAX_SPOT_LIGHTS ];
-	uniform float spotLightDecay[ MAX_SPOT_LIGHTS ];
-
-#endif
-
 #if MAX_SPOT_LIGHTS > 0 || defined( USE_ENVMAP )
 
 	varying vec3 vWorldPosition;

+ 112 - 0
src/renderers/shaders/ShaderChunk/lights_standard_fragment.glsl

@@ -0,0 +1,112 @@
+vec3 viewDir = normalize( vViewPosition );
+
+vec3 totalDiffuseLight = vec3( 0.0 );
+vec3 totalSpecularLight = vec3( 0.0 );
+
+
+// roughness linear remapping
+
+roughnessFactor = roughnessFactor * 0.5 + 0.5; // disney's remapping of [ 0, 1 ] roughness to [ 0.5, 1 ]
+
+
+// metalness effect on color
+
+vec3 specularColor = mix( vec3( 0.04 ), diffuseColor.rgb, metalnessFactor );
+
+diffuseColor.rgb *= ( 1.0 - metalnessFactor );
+
+
+#if MAX_POINT_LIGHTS > 0
+
+	for ( int i = 0; i < MAX_POINT_LIGHTS; i ++ ) {
+
+		vec3 lightColor = pointLightColor[ i ];
+
+		vec3 lightPosition = pointLightPosition[ i ];
+		vec3 lVector = lightPosition + vViewPosition.xyz;
+		vec3 lightDir = normalize( lVector );
+
+		// attenuation
+
+		float attenuation = calcLightAttenuation( length( lVector ), pointLightDistance[ i ], pointLightDecay[ i ] );
+
+		// diffuse
+
+		float cosineTerm = saturate( dot( normal, lightDir ) );
+
+		totalDiffuseLight += lightColor * attenuation * cosineTerm;
+
+		// specular
+
+    	vec3 brdf = BRDF_GGX( specularColor, roughnessFactor, normal, lightDir, viewDir );
+
+		totalSpecularLight += brdf * specularStrength * lightColor * attenuation * cosineTerm;
+
+
+	}
+
+#endif
+
+#if MAX_SPOT_LIGHTS > 0
+
+	for ( int i = 0; i < MAX_SPOT_LIGHTS; i ++ ) {
+
+		vec3 lightColor = spotLightColor[ i ];
+
+		vec3 lightPosition = spotLightPosition[ i ];
+		vec3 lVector = lightPosition + vViewPosition.xyz;
+		vec3 lightDir = normalize( lVector );
+
+		float spotEffect = dot( spotLightDirection[ i ], lightDir );
+
+		if ( spotEffect > spotLightAngleCos[ i ] ) {
+
+			spotEffect = saturate( pow( saturate( spotEffect ), spotLightExponent[ i ] ) );
+
+			// attenuation
+
+			float attenuation = calcLightAttenuation( length( lVector ), spotLightDistance[ i ], spotLightDecay[ i ] );
+
+			attenuation *= spotEffect;
+
+			// diffuse
+
+			float cosineTerm = saturate( dot( normal, lightDir ) );
+
+			totalDiffuseLight += lightColor * attenuation * cosineTerm;
+
+			// specular
+
+    		vec3 brdf = BRDF_GGX( specularColor, roughnessFactor, normal, lightDir, viewDir );
+
+			totalSpecularLight += brdf * specularStrength * lightColor * attenuation * cosineTerm;
+
+		}
+
+	}
+
+#endif
+
+#if MAX_DIR_LIGHTS > 0
+
+	for ( int i = 0; i < MAX_DIR_LIGHTS; i ++ ) {
+
+		vec3 lightColor = directionalLightColor[ i ];
+
+		vec3 lightDir = directionalLightDirection[ i ];
+
+		// diffuse
+
+		float cosineTerm = saturate( dot( normal, lightDir ) );
+
+		totalDiffuseLight += lightColor * cosineTerm;
+
+		// specular
+
+    	vec3 brdf = BRDF_GGX( specularColor, roughnessFactor, normal, lightDir, viewDir );
+
+		totalSpecularLight += brdf * specularStrength * lightColor * cosineTerm;
+
+	}
+
+#endif

+ 2 - 2
src/renderers/shaders/ShaderChunk/logdepthbuf_vertex.glsl

@@ -6,10 +6,10 @@
 
 		vFragDepth = 1.0 + gl_Position.w;
 
-#else
+	#else
 
 		gl_Position.z = (gl_Position.z - 1.0) * gl_Position.w;
 
 	#endif
 
-#endif
+#endif

+ 8 - 0
src/renderers/shaders/ShaderChunk/metalnessmap_fragment.glsl

@@ -0,0 +1,8 @@
+float metalnessFactor = metalness;
+
+#ifdef USE_METALNESSMAP
+
+	vec4 texelMetalness = texture2D( metalnessMap, vUv );
+	metalnessFactor = texelMetalness.r; // note equality, not *=
+
+#endif

+ 5 - 0
src/renderers/shaders/ShaderChunk/metalnessmap_pars_fragment.glsl

@@ -0,0 +1,5 @@
+#ifdef USE_METALNESSMAP
+
+	uniform sampler2D metalnessMap;
+
+#endif

+ 8 - 0
src/renderers/shaders/ShaderChunk/roughnessmap_fragment.glsl

@@ -0,0 +1,8 @@
+float roughnessFactor = roughness;
+
+#ifdef USE_ROUGHNESSMAP
+
+	vec4 texelRoughness = texture2D( roughnessMap, vUv );
+	roughnessFactor *= texelRoughness.r;
+
+#endif

+ 5 - 0
src/renderers/shaders/ShaderChunk/roughnessmap_pars_fragment.glsl

@@ -0,0 +1,5 @@
+#ifdef USE_ROUGHNESSMAP
+
+	uniform sampler2D roughnessMap;
+
+#endif

+ 1 - 1
src/renderers/shaders/ShaderChunk/uv_pars_fragment.glsl

@@ -1,4 +1,4 @@
-#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP ) || defined( USE_EMISSIVEMAP )
+#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP ) || defined( USE_EMISSIVEMAP ) || defined( USE_ROUGHNESSMAP ) || defined( USE_REFLECTIVITYMAP ) || defined( USE_METALNESSMAP )
 
 	varying vec2 vUv;
 

+ 1 - 1
src/renderers/shaders/ShaderChunk/uv_pars_vertex.glsl

@@ -1,4 +1,4 @@
-#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP ) || defined( USE_EMISSIVEMAP )
+#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP ) || defined( USE_EMISSIVEMAP ) || defined( USE_ROUGHNESSMAP ) || defined( USE_REFLECTIVITYMAP ) || defined( USE_METALNESSMAP )
 
 	varying vec2 vUv;
 	uniform vec4 offsetRepeat;

+ 1 - 1
src/renderers/shaders/ShaderChunk/uv_vertex.glsl

@@ -1,4 +1,4 @@
-#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP ) || defined( USE_EMISSIVEMAP )
+#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP ) || defined( USE_EMISSIVEMAP ) || defined( USE_ROUGHNESSMAP ) || defined( USE_REFLECTIVITYMAP ) || defined( USE_METALNESSMAP )
 
 	vUv = uv * offsetRepeat.zw + offsetRepeat.xy;
 

+ 1 - 1
src/renderers/shaders/ShaderChunk/worldpos_vertex.glsl

@@ -1,4 +1,4 @@
-#if defined( USE_ENVMAP ) || defined( PHONG ) || defined( LAMBERT ) || defined ( USE_SHADOWMAP )
+#if defined( USE_ENVMAP ) || defined( PHONG ) || defined( STANDARD ) || defined( LAMBERT ) || defined ( USE_SHADOWMAP )
 
 	#ifdef USE_SKINNING
 

+ 167 - 1
src/renderers/shaders/ShaderLib.js

@@ -143,7 +143,7 @@ THREE.ShaderLib = {
 			THREE.ShaderChunk[ "uv_pars_vertex" ],
 			THREE.ShaderChunk[ "uv2_pars_vertex" ],
 			THREE.ShaderChunk[ "envmap_pars_vertex" ],
-			THREE.ShaderChunk[ "lights_lambert_pars_vertex" ],
+			THREE.ShaderChunk[ "lights_pars" ],
 			THREE.ShaderChunk[ "color_pars_vertex" ],
 			THREE.ShaderChunk[ "morphtarget_pars_vertex" ],
 			THREE.ShaderChunk[ "skinning_pars_vertex" ],
@@ -351,6 +351,7 @@ THREE.ShaderLib = {
 			THREE.ShaderChunk[ "emissivemap_pars_fragment" ],
 			THREE.ShaderChunk[ "envmap_pars_fragment" ],
 			THREE.ShaderChunk[ "fog_pars_fragment" ],
+			THREE.ShaderChunk[ "lights_pars" ],
 			THREE.ShaderChunk[ "lights_phong_pars_fragment" ],
 			THREE.ShaderChunk[ "shadowmap_pars_fragment" ],
 			THREE.ShaderChunk[ "bumpmap_pars_fragment" ],
@@ -408,6 +409,171 @@ THREE.ShaderLib = {
 
 	},
 
+	'standard': {
+
+		uniforms: THREE.UniformsUtils.merge( [
+
+			THREE.UniformsLib[ "common" ],
+			THREE.UniformsLib[ "aomap" ],
+			THREE.UniformsLib[ "lightmap" ],
+			THREE.UniformsLib[ "emissivemap" ],
+			THREE.UniformsLib[ "bumpmap" ],
+			THREE.UniformsLib[ "normalmap" ],
+			THREE.UniformsLib[ "displacementmap" ],
+			THREE.UniformsLib[ "roughnessmap" ],
+			THREE.UniformsLib[ "reflectivitymap" ],
+			THREE.UniformsLib[ "metalnessmap" ],
+			THREE.UniformsLib[ "fog" ],
+			THREE.UniformsLib[ "lights" ],
+			THREE.UniformsLib[ "shadowmap" ],
+
+			{
+				"emissive" : { type: "c", value: new THREE.Color( 0x000000 ) },
+				"roughness": { type: "f", value: 0.5 },
+				"metalness": { type: "f", value: 0 },
+				"envMapIntensity" : { type: "f", value: 1 } // temporary
+			}
+
+		] ),
+
+		vertexShader: [
+
+			"#define STANDARD",
+
+			"varying vec3 vViewPosition;",
+
+			"#ifndef FLAT_SHADED",
+
+			"	varying vec3 vNormal;",
+
+			"#endif",
+
+			THREE.ShaderChunk[ "common" ],
+			THREE.ShaderChunk[ "uv_pars_vertex" ],
+			THREE.ShaderChunk[ "uv2_pars_vertex" ],
+			THREE.ShaderChunk[ "displacementmap_pars_vertex" ],
+			THREE.ShaderChunk[ "envmap_pars_vertex" ],
+			THREE.ShaderChunk[ "lights_phong_pars_vertex" ], // use phong chunk for now
+			THREE.ShaderChunk[ "color_pars_vertex" ],
+			THREE.ShaderChunk[ "morphtarget_pars_vertex" ],
+			THREE.ShaderChunk[ "skinning_pars_vertex" ],
+			THREE.ShaderChunk[ "shadowmap_pars_vertex" ],
+			THREE.ShaderChunk[ "specularmap_pars_fragment" ],
+			THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ],
+
+			"void main() {", // STANDARD
+
+				THREE.ShaderChunk[ "uv_vertex" ],
+				THREE.ShaderChunk[ "uv2_vertex" ],
+				THREE.ShaderChunk[ "color_vertex" ],
+
+				THREE.ShaderChunk[ "beginnormal_vertex" ],
+				THREE.ShaderChunk[ "morphnormal_vertex" ],
+				THREE.ShaderChunk[ "skinbase_vertex" ],
+				THREE.ShaderChunk[ "skinnormal_vertex" ],
+				THREE.ShaderChunk[ "defaultnormal_vertex" ],
+
+			"#ifndef FLAT_SHADED", // Normal computed with derivatives when FLAT_SHADED
+
+			"	vNormal = normalize( transformedNormal );",
+
+			"#endif",
+
+				THREE.ShaderChunk[ "begin_vertex" ],
+				THREE.ShaderChunk[ "displacementmap_vertex" ],
+				THREE.ShaderChunk[ "morphtarget_vertex" ],
+				THREE.ShaderChunk[ "skinning_vertex" ],
+				THREE.ShaderChunk[ "project_vertex" ],
+				THREE.ShaderChunk[ "logdepthbuf_vertex" ],
+
+			"	vViewPosition = - mvPosition.xyz;",
+
+				THREE.ShaderChunk[ "worldpos_vertex" ],
+				THREE.ShaderChunk[ "envmap_vertex" ],
+				THREE.ShaderChunk[ "lights_phong_vertex" ], // use phong chunk for now
+				THREE.ShaderChunk[ "shadowmap_vertex" ],
+
+			"}"
+
+		].join( "\n" ),
+
+		fragmentShader: [
+
+			"#define STANDARD",
+
+			"uniform vec3 diffuse;",
+			"uniform vec3 emissive;",
+			"uniform float roughness;",
+			"uniform float metalness;",
+			"uniform float opacity;",
+
+			"uniform float envMapIntensity;", // temporary
+
+			THREE.ShaderChunk[ "common" ],
+			THREE.ShaderChunk[ "color_pars_fragment" ],
+			THREE.ShaderChunk[ "uv_pars_fragment" ],
+			THREE.ShaderChunk[ "uv2_pars_fragment" ],
+			THREE.ShaderChunk[ "map_pars_fragment" ],
+			THREE.ShaderChunk[ "alphamap_pars_fragment" ],
+			THREE.ShaderChunk[ "aomap_pars_fragment" ],
+			THREE.ShaderChunk[ "lightmap_pars_fragment" ],
+			THREE.ShaderChunk[ "emissivemap_pars_fragment" ],
+			THREE.ShaderChunk[ "envmap_pars_fragment" ],
+			THREE.ShaderChunk[ "fog_pars_fragment" ],
+			THREE.ShaderChunk[ "lights_phong_pars_fragment" ], // use phong chunk for now
+			THREE.ShaderChunk[ "shadowmap_pars_fragment" ],
+			THREE.ShaderChunk[ "bumpmap_pars_fragment" ],
+			THREE.ShaderChunk[ "normalmap_pars_fragment" ],
+			THREE.ShaderChunk[ "roughnessmap_pars_fragment" ],
+			//THREE.ShaderChunk[ "reflectivitymap_pars_fragment" ],
+			THREE.ShaderChunk[ "metalnessmap_pars_fragment" ],
+			THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ],
+
+			"void main() {",
+
+			"	vec3 outgoingLight = vec3( 0.0 );",
+			"	vec4 diffuseColor = vec4( diffuse, opacity );",
+			"	vec3 totalAmbientLight = ambientLightColor;",
+			"	vec3 totalEmissiveLight = emissive;",
+			"	vec3 shadowMask = vec3( 1.0 );",
+
+				THREE.ShaderChunk[ "logdepthbuf_fragment" ],
+				THREE.ShaderChunk[ "map_fragment" ],
+				THREE.ShaderChunk[ "color_fragment" ],
+				THREE.ShaderChunk[ "alphamap_fragment" ],
+				THREE.ShaderChunk[ "alphatest_fragment" ],
+				THREE.ShaderChunk[ "specularmap_fragment" ],
+				THREE.ShaderChunk[ "roughnessmap_fragment" ],
+				//THREE.ShaderChunk[ "reflectivitymap_fragment" ],
+				THREE.ShaderChunk[ "metalnessmap_fragment" ],
+				THREE.ShaderChunk[ "normal_phong_fragment" ], // use phong chunk for now
+				THREE.ShaderChunk[ "lightmap_fragment" ],
+				THREE.ShaderChunk[ "hemilight_fragment" ],
+				THREE.ShaderChunk[ "aomap_fragment" ],
+				THREE.ShaderChunk[ "emissivemap_fragment" ],
+
+				THREE.ShaderChunk[ "lights_standard_fragment" ],
+				THREE.ShaderChunk[ "shadowmap_fragment" ],
+
+				"totalDiffuseLight *= shadowMask;",
+				"totalSpecularLight *= shadowMask;",
+
+				"outgoingLight += diffuseColor.rgb * ( totalDiffuseLight + totalAmbientLight ) + totalSpecularLight + totalEmissiveLight;",
+
+				THREE.ShaderChunk[ "envmap_standard_fragment" ],
+
+				THREE.ShaderChunk[ "linear_to_gamma_fragment" ],
+
+				THREE.ShaderChunk[ "fog_fragment" ],
+
+			"	gl_FragColor = vec4( outgoingLight, diffuseColor.a );",
+
+			"}"
+
+		].join( "\n" )
+
+	},
+
 	'points': {
 
 		uniforms: THREE.UniformsUtils.merge( [

+ 18 - 0
src/renderers/shaders/UniformsLib.js

@@ -64,6 +64,24 @@ THREE.UniformsLib = {
 
 	},
 
+	roughnessmap: {
+
+		"roughnessMap" : { type: "t", value: null }
+
+	},
+
+	reflectivitymap: {
+
+		"reflectivityMap" : { type: "t", value: null }
+
+	},
+
+	metalnessmap: {
+
+		"metalnessMap" : { type: "t", value: null }
+
+	},
+
 	fog : {
 
 		"fogDensity" : { type: "f", value: 0.00025 },

+ 10 - 0
src/renderers/webgl/WebGLProgram.js

@@ -203,6 +203,9 @@ THREE.WebGLProgram = ( function () {
 				parameters.normalMap ? '#define USE_NORMALMAP' : '',
 				parameters.displacementMap && parameters.supportsVertexTextures ? '#define USE_DISPLACEMENTMAP' : '',
 				parameters.specularMap ? '#define USE_SPECULARMAP' : '',
+				parameters.roughnessMap ? '#define USE_ROUGHNESSMAP' : '',
+				parameters.reflectivityMap ? '#define USE_REFLECTIVITYMAP' : '',
+				parameters.metalnessMap ? '#define USE_METALNESSMAP' : '',
 				parameters.alphaMap ? '#define USE_ALPHAMAP' : '',
 				parameters.vertexColors ? '#define USE_COLOR' : '',
 
@@ -285,6 +288,8 @@ THREE.WebGLProgram = ( function () {
 				parameters.bumpMap || parameters.normalMap || parameters.flatShading || material.derivatives ? '#extension GL_OES_standard_derivatives : enable' : '',
 				parameters.logarithmicDepthBuffer && renderer.extensions.get( 'EXT_frag_depth' ) ? '#extension GL_EXT_frag_depth : enable' : '',
 
+				parameters.envMap && renderer.extensions.get( 'EXT_shader_texture_lod' ) ? '#extension GL_EXT_shader_texture_lod : enable' : '',
+
 				'precision ' + parameters.precision + ' float;',
 				'precision ' + parameters.precision + ' int;',
 
@@ -319,6 +324,9 @@ THREE.WebGLProgram = ( function () {
 				parameters.bumpMap ? '#define USE_BUMPMAP' : '',
 				parameters.normalMap ? '#define USE_NORMALMAP' : '',
 				parameters.specularMap ? '#define USE_SPECULARMAP' : '',
+				parameters.roughnessMap ? '#define USE_ROUGHNESSMAP' : '',
+				parameters.reflectivityMap ? '#define USE_REFLECTIVITYMAP' : '',
+				parameters.metalnessMap ? '#define USE_METALNESSMAP' : '',
 				parameters.alphaMap ? '#define USE_ALPHAMAP' : '',
 				parameters.vertexColors ? '#define USE_COLOR' : '',
 
@@ -336,6 +344,8 @@ THREE.WebGLProgram = ( function () {
 				parameters.logarithmicDepthBuffer ? '#define USE_LOGDEPTHBUF' : '',
 				parameters.logarithmicDepthBuffer && renderer.extensions.get( 'EXT_frag_depth' ) ? '#define USE_LOGDEPTHBUF_EXT' : '',
 
+				parameters.envMap && renderer.extensions.get( 'EXT_shader_texture_lod' ) ? '#define TEXTURE_CUBE_LOD_EXT' : '',
+
 				'uniform mat4 viewMatrix;',
 				'uniform vec3 cameraPosition;',
 

+ 5 - 0
src/renderers/webgl/WebGLPrograms.js

@@ -8,6 +8,7 @@ THREE.WebGLPrograms = function ( renderer, capabilities ) {
 		MeshBasicMaterial: 'basic',
 		MeshLambertMaterial: 'lambert',
 		MeshPhongMaterial: 'phong',
+		MeshStandardMaterial: 'standard',
 		LineBasicMaterial: 'basic',
 		LineDashedMaterial: 'dashed',
 		PointsMaterial: 'points'
@@ -16,6 +17,7 @@ THREE.WebGLPrograms = function ( renderer, capabilities ) {
 	var parameterNames = [
 		"precision", "supportsVertexTextures", "map", "envMap", "envMapMode",
 		"lightMap", "aoMap", "emissiveMap", "bumpMap", "normalMap", "displacementMap", "specularMap",
+		"roughnessMap", "reflectivityMap", "metalnessMap",
 		"alphaMap", "combine", "vertexColors", "fog", "useFog", "fogExp",
 		"flatShading", "sizeAttenuation", "logarithmicDepthBuffer", "skinning",
 		"maxBones", "useVertexTexture", "morphTargets", "morphNormals",
@@ -152,6 +154,9 @@ THREE.WebGLPrograms = function ( renderer, capabilities ) {
 			bumpMap: !! material.bumpMap,
 			normalMap: !! material.normalMap,
 			displacementMap: !! material.displacementMap,
+			roughnessMap: !! material.roughnessMap,
+			reflectivityMap: !! material.reflectivityMap,
+			metalnessMap: !! material.metalnessMap,
 			specularMap: !! material.specularMap,
 			alphaMap: !! material.alphaMap,
 

+ 10 - 10
test/unit/math/Box2.js

@@ -189,23 +189,23 @@ test( "distanceToPoint", function() {
 	ok( b.distanceToPoint( new THREE.Vector2( -2, -2 ) ) == Math.sqrt( 2 ), "Passed!" );
 });
 
-test( "isIntersectionBox", function() {
+test( "intersectsBox", function() {
 	var a = new THREE.Box2( zero2.clone(), zero2.clone() );
 	var b = new THREE.Box2( zero2.clone(), one2.clone() );
 	var c = new THREE.Box2( one2.clone().negate(), one2.clone() );
 
-	ok( a.isIntersectionBox( a ), "Passed!" );
-	ok( a.isIntersectionBox( b ), "Passed!" );
-	ok( a.isIntersectionBox( c ), "Passed!" );
+	ok( a.intersectsBox( a ), "Passed!" );
+	ok( a.intersectsBox( b ), "Passed!" );
+	ok( a.intersectsBox( c ), "Passed!" );
 
-	ok( b.isIntersectionBox( a ), "Passed!" );
-	ok( c.isIntersectionBox( a ), "Passed!" );
-	ok( b.isIntersectionBox( c ), "Passed!" );
+	ok( b.intersectsBox( a ), "Passed!" );
+	ok( c.intersectsBox( a ), "Passed!" );
+	ok( b.intersectsBox( c ), "Passed!" );
 
 	b.translate( new THREE.Vector2( 2, 2 ) );
-	ok( ! a.isIntersectionBox( b ), "Passed!" );
-	ok( ! b.isIntersectionBox( a ), "Passed!" );
-	ok( ! b.isIntersectionBox( c ), "Passed!" );
+	ok( ! a.intersectsBox( b ), "Passed!" );
+	ok( ! b.intersectsBox( a ), "Passed!" );
+	ok( ! b.intersectsBox( c ), "Passed!" );
 });
 
 test( "intersect", function() {

+ 10 - 10
test/unit/math/Box3.js

@@ -204,23 +204,23 @@ test( "distanceToPoint", function() {
 	ok( b.distanceToPoint( new THREE.Vector3( -2, -2, -2 ) ) == Math.sqrt( 3 ), "Passed!" );
 });
 
-test( "isIntersectionBox", function() {
+test( "intersectsBox", function() {
 	var a = new THREE.Box3( zero3.clone(), zero3.clone() );
 	var b = new THREE.Box3( zero3.clone(), one3.clone() );
 	var c = new THREE.Box3( one3.clone().negate(), one3.clone() );
 
-	ok( a.isIntersectionBox( a ), "Passed!" );
-	ok( a.isIntersectionBox( b ), "Passed!" );
-	ok( a.isIntersectionBox( c ), "Passed!" );
+	ok( a.intersectsBox( a ), "Passed!" );
+	ok( a.intersectsBox( b ), "Passed!" );
+	ok( a.intersectsBox( c ), "Passed!" );
 
-	ok( b.isIntersectionBox( a ), "Passed!" );
-	ok( c.isIntersectionBox( a ), "Passed!" );
-	ok( b.isIntersectionBox( c ), "Passed!" );
+	ok( b.intersectsBox( a ), "Passed!" );
+	ok( c.intersectsBox( a ), "Passed!" );
+	ok( b.intersectsBox( c ), "Passed!" );
 
 	b.translate( new THREE.Vector3( 2, 2, 2 ) );
-	ok( ! a.isIntersectionBox( b ), "Passed!" );
-	ok( ! b.isIntersectionBox( a ), "Passed!" );
-	ok( ! b.isIntersectionBox( c ), "Passed!" );
+	ok( ! a.intersectsBox( b ), "Passed!" );
+	ok( ! b.intersectsBox( a ), "Passed!" );
+	ok( ! b.intersectsBox( c ), "Passed!" );
 });
 
 test( "getBoundingSphere", function() {

+ 11 - 11
test/unit/math/Plane.js

@@ -81,14 +81,14 @@ test( "setComponents", function() {
 test( "setFromNormalAndCoplanarPoint", function() {
 	var normal = one3.clone().normalize();
 	var a = new THREE.Plane().setFromNormalAndCoplanarPoint( normal, zero3 );
-	
+
 	ok( a.normal.equals( normal ), "Passed!" );
 	ok( a.constant == 0, "Passed!" );
 });
 
 test( "normalize", function() {
 	var a = new THREE.Plane( new THREE.Vector3( 2, 0, 0 ), 2 );
-	
+
 	a.normalize();
 	ok( a.normal.length() == 1, "Passed!" );
 	ok( a.normal.equals( new THREE.Vector3( 1, 0, 0 ) ), "Passed!" );
@@ -97,7 +97,7 @@ test( "normalize", function() {
 
 test( "negate/distanceToPoint", function() {
 	var a = new THREE.Plane( new THREE.Vector3( 2, 0, 0 ), -2 );
-	
+
 	a.normalize();
 	ok( a.distanceToPoint( new THREE.Vector3( 4, 0, 0 ) ) === 3, "Passed!" );
 	ok( a.distanceToPoint( new THREE.Vector3( 1, 0, 0 ) ) === 0, "Passed!" );
@@ -109,7 +109,7 @@ test( "negate/distanceToPoint", function() {
 
 test( "distanceToPoint", function() {
 	var a = new THREE.Plane( new THREE.Vector3( 2, 0, 0 ), -2 );
-	
+
 	a.normalize();
 	ok( a.distanceToPoint( a.projectPoint( zero3.clone() ) ) === 0, "Passed!" );
 	ok( a.distanceToPoint( new THREE.Vector3( 4, 0, 0 ) ) === 3, "Passed!" );
@@ -119,7 +119,7 @@ test( "distanceToSphere", function() {
 	var a = new THREE.Plane( new THREE.Vector3( 1, 0, 0 ), 0 );
 
 	var b = new THREE.Sphere( new THREE.Vector3( 2, 0, 0 ), 1 );
-	
+
 	ok( a.distanceToSphere( b ) === 1, "Passed!" );
 
 	a.set( new THREE.Vector3( 1, 0, 0 ), 2 );
@@ -132,23 +132,23 @@ test( "isInterestionLine/intersectLine", function() {
 	var a = new THREE.Plane( new THREE.Vector3( 1, 0, 0 ), 0 );
 
 	var l1 = new THREE.Line3( new THREE.Vector3( -10, 0, 0 ), new THREE.Vector3( 10, 0, 0 ) );
-	ok( a.isIntersectionLine( l1 ), "Passed!" );
+	ok( a.intersectsLine( l1 ), "Passed!" );
 	ok( a.intersectLine( l1 ).equals( new THREE.Vector3( 0, 0, 0 ) ), "Passed!" );
 
 	a = new THREE.Plane( new THREE.Vector3( 1, 0, 0 ), -3 );
 
-	ok( a.isIntersectionLine( l1 ), "Passed!" );
+	ok( a.intersectsLine( l1 ), "Passed!" );
 	ok( a.intersectLine( l1 ).equals( new THREE.Vector3( 3, 0, 0 ) ), "Passed!" );
 
 
 	a = new THREE.Plane( new THREE.Vector3( 1, 0, 0 ), -11 );
 
-	ok( ! a.isIntersectionLine( l1 ), "Passed!" );
+	ok( ! a.intersectsLine( l1 ), "Passed!" );
 	ok( a.intersectLine( l1 ) === undefined, "Passed!" );
-	
+
 	a = new THREE.Plane( new THREE.Vector3( 1, 0, 0 ), 11 );
 
-	ok( ! a.isIntersectionLine( l1 ), "Passed!" );
+	ok( ! a.intersectsLine( l1 ), "Passed!" );
 	ok( a.intersectLine( l1 ) === undefined, "Passed!" );
 
 });
@@ -162,7 +162,7 @@ test( "projectPoint", function() {
 	a = new THREE.Plane( new THREE.Vector3( 0, 1, 0 ), -1 );
 	ok( a.projectPoint( new THREE.Vector3( 0, 0, 0 ) ).equals( new THREE.Vector3( 0, 1, 0 ) ), "Passed!" );
 	ok( a.projectPoint( new THREE.Vector3( 0, 1, 0 ) ).equals( new THREE.Vector3( 0, 1, 0 ) ), "Passed!" );
-	
+
 });
 
 test( "orthoPoint", function() {

+ 44 - 46
test/unit/math/Ray.js

@@ -109,7 +109,7 @@ test( "distanceSqToPoint", function() {
 	ok( d === 0, "Passed!" );
 });
 
-test( "isIntersectionSphere", function() {
+test( "intersectsSphere", function() {
 	var a = new THREE.Ray( one3.clone(), new THREE.Vector3( 0, 0, 1 ) );
 	var b = new THREE.Sphere( zero3, 0.5 );
 	var c = new THREE.Sphere( zero3, 1.5 );
@@ -117,17 +117,17 @@ test( "isIntersectionSphere", function() {
 	var e = new THREE.Sphere( two3, 0.1 );
 	var f = new THREE.Sphere( two3, 1 );
 
-	ok( ! a.isIntersectionSphere( b ), "Passed!" );
-	ok( ! a.isIntersectionSphere( c ), "Passed!" );
-	ok( a.isIntersectionSphere( d ), "Passed!" );
-	ok( ! a.isIntersectionSphere( e ), "Passed!" );
-	ok( ! a.isIntersectionSphere( f ), "Passed!" );
+	ok( ! a.intersectsSphere( b ), "Passed!" );
+	ok( ! a.intersectsSphere( c ), "Passed!" );
+	ok( a.intersectsSphere( d ), "Passed!" );
+	ok( ! a.intersectsSphere( e ), "Passed!" );
+	ok( ! a.intersectsSphere( f ), "Passed!" );
 });
 
 test( "intersectSphere", function() {
-	
+
 	var TOL = 0.0001;
-	
+
 	// ray a0 origin located at ( 0, 0, 0 ) and points outward in negative-z direction
 	var a0 = new THREE.Ray( zero3.clone(), new THREE.Vector3( 0, 0, -1 ) );
 	// ray a1 origin located at ( 1, 1, 1 ) and points left in negative-x direction
@@ -136,75 +136,75 @@ test( "intersectSphere", function() {
 	// sphere (radius of 2) located behind ray a0, should result in null
 	var b = new THREE.Sphere( new THREE.Vector3( 0, 0, 3 ), 2 );
 	ok( a0.intersectSphere( b ) === null, "Passed!" );
-	
+
 	// sphere (radius of 2) located in front of, but too far right of ray a0, should result in null
 	var b = new THREE.Sphere( new THREE.Vector3( 3, 0, -1 ), 2 );
 	ok( a0.intersectSphere( b ) === null, "Passed!" );
-	
+
 	// sphere (radius of 2) located below ray a1, should result in null
 	var b = new THREE.Sphere( new THREE.Vector3( 1, -2, 1 ), 2 );
 	ok( a1.intersectSphere( b ) === null, "Passed!" );
-	
-	// sphere (radius of 1) located to the left of ray a1, should result in intersection at 0, 1, 1 
+
+	// sphere (radius of 1) located to the left of ray a1, should result in intersection at 0, 1, 1
 	var b = new THREE.Sphere( new THREE.Vector3( -1, 1, 1 ), 1 );
 	ok( a1.intersectSphere( b ).distanceTo( new THREE.Vector3( 0, 1, 1 ) ) < TOL, "Passed!" );
-	
-	// sphere (radius of 1) located in front of ray a0, should result in intersection at 0, 0, -1 
+
+	// sphere (radius of 1) located in front of ray a0, should result in intersection at 0, 0, -1
 	var b = new THREE.Sphere( new THREE.Vector3( 0, 0, -2 ), 1 );
 	ok( a0.intersectSphere( b ).distanceTo( new THREE.Vector3( 0, 0, -1 ) ) < TOL, "Passed!" );
-	
-	// sphere (radius of 2) located in front & right of ray a0, should result in intersection at 0, 0, -1, or left-most edge of sphere 
+
+	// sphere (radius of 2) located in front & right of ray a0, should result in intersection at 0, 0, -1, or left-most edge of sphere
 	var b = new THREE.Sphere( new THREE.Vector3( 2, 0, -1 ), 2 );
 	ok( a0.intersectSphere( b ).distanceTo( new THREE.Vector3( 0, 0, -1 ) ) < TOL, "Passed!" );
-	
-	// same situation as above, but move the sphere a fraction more to the right, and ray a0 should now just miss 
+
+	// same situation as above, but move the sphere a fraction more to the right, and ray a0 should now just miss
 	var b = new THREE.Sphere( new THREE.Vector3( 2.01, 0, -1 ), 2 );
 	ok( a0.intersectSphere( b ) === null, "Passed!" );
-	
+
 	// following tests are for situations where the ray origin is inside the sphere
-	
-	// sphere (radius of 1) center located at ray a0 origin / sphere surrounds the ray origin, so the first intersect point 0, 0, 1, 
+
+	// sphere (radius of 1) center located at ray a0 origin / sphere surrounds the ray origin, so the first intersect point 0, 0, 1,
 	// is behind ray a0.  Therefore, second exit point on back of sphere will be returned: 0, 0, -1
 	// thus keeping the intersection point always in front of the ray.
 	var b = new THREE.Sphere( zero3.clone(), 1 );
 	ok( a0.intersectSphere( b ).distanceTo( new THREE.Vector3( 0, 0, -1 ) ) < TOL, "Passed!" );
-	
-	// sphere (radius of 4) center located behind ray a0 origin / sphere surrounds the ray origin, so the first intersect point 0, 0, 5, 
+
+	// sphere (radius of 4) center located behind ray a0 origin / sphere surrounds the ray origin, so the first intersect point 0, 0, 5,
 	// is behind ray a0.  Therefore, second exit point on back of sphere will be returned: 0, 0, -3
 	// thus keeping the intersection point always in front of the ray.
 	var b = new THREE.Sphere( new THREE.Vector3( 0, 0, 1 ), 4 );
 	ok( a0.intersectSphere( b ).distanceTo( new THREE.Vector3( 0, 0, -3 ) ) < TOL, "Passed!" );
-	
-	// sphere (radius of 4) center located in front of ray a0 origin / sphere surrounds the ray origin, so the first intersect point 0, 0, 3, 
+
+	// sphere (radius of 4) center located in front of ray a0 origin / sphere surrounds the ray origin, so the first intersect point 0, 0, 3,
 	// is behind ray a0.  Therefore, second exit point on back of sphere will be returned: 0, 0, -5
 	// thus keeping the intersection point always in front of the ray.
 	var b = new THREE.Sphere( new THREE.Vector3( 0, 0, -1 ), 4 );
 	ok( a0.intersectSphere( b ).distanceTo( new THREE.Vector3( 0, 0, -5 ) ) < TOL, "Passed!" );
-	
+
 });
 
-test( "isIntersectionPlane", function() {
+test( "intersectsPlane", function() {
 	var a = new THREE.Ray( one3.clone(), new THREE.Vector3( 0, 0, 1 ) );
 
 	// parallel plane in front of the ray
 	var b = new THREE.Plane().setFromNormalAndCoplanarPoint( new THREE.Vector3( 0, 0, 1 ), one3.clone().sub( new THREE.Vector3( 0, 0, -1 ) ) );
-	ok( a.isIntersectionPlane( b ), "Passed!" );
+	ok( a.intersectsPlane( b ), "Passed!" );
 
 	// parallel plane coincident with origin
 	var c = new THREE.Plane().setFromNormalAndCoplanarPoint( new THREE.Vector3( 0, 0, 1 ), one3.clone().sub( new THREE.Vector3( 0, 0, 0 ) ) );
-	ok( a.isIntersectionPlane( c ), "Passed!" );
+	ok( a.intersectsPlane( c ), "Passed!" );
 
 	// parallel plane behind the ray
 	var d = new THREE.Plane().setFromNormalAndCoplanarPoint( new THREE.Vector3( 0, 0, 1 ), one3.clone().sub( new THREE.Vector3( 0, 0, 1 ) ) );
-	ok( ! a.isIntersectionPlane( d ), "Passed!" );
+	ok( ! a.intersectsPlane( d ), "Passed!" );
 
 	// perpendical ray that overlaps exactly
 	var e = new THREE.Plane().setFromNormalAndCoplanarPoint( new THREE.Vector3( 1, 0, 0 ), one3 );
-	ok( a.isIntersectionPlane( e ), "Passed!" );
+	ok( a.intersectsPlane( e ), "Passed!" );
 
 	// perpendical ray that doesn't overlap
 	var f = new THREE.Plane().setFromNormalAndCoplanarPoint( new THREE.Vector3( 1, 0, 0 ), zero3 );
-	ok( ! a.isIntersectionPlane( f ), "Passed!" );
+	ok( ! a.intersectsPlane( f ), "Passed!" );
 });
 
 test( "intersectPlane", function() {
@@ -295,39 +295,37 @@ test( "distanceSqToSegment", function() {
 test( "intersectBox", function() {
 
 	var TOL = 0.0001;
-	
+
 	var box = new THREE.Box3( new THREE.Vector3(  -1, -1, -1 ), new THREE.Vector3( 1, 1, 1 ) );
 
 	var a = new THREE.Ray( new THREE.Vector3( -2, 0, 0 ), new THREE.Vector3( 1, 0, 0) );
 	//ray should intersect box at -1,0,0
-	ok( a.isIntersectionBox(box) === true, "Passed!" );
+	ok( a.intersectsBox(box) === true, "Passed!" );
 	ok( a.intersectBox(box).distanceTo( new THREE.Vector3( -1, 0, 0 ) ) < TOL, "Passed!" );
 
 	var b = new THREE.Ray( new THREE.Vector3( -2, 0, 0 ), new THREE.Vector3( -1, 0, 0) );
 	//ray is point away from box, it should not intersect
-	ok( b.isIntersectionBox(box) === false, "Passed!" );
+	ok( b.intersectsBox(box) === false, "Passed!" );
 	ok( b.intersectBox(box) === null, "Passed!" );
 
 	var c = new THREE.Ray( new THREE.Vector3( 0, 0, 0 ), new THREE.Vector3( 1, 0, 0) );
 	// ray is inside box, should return exit point
-	ok( c.isIntersectionBox(box) === true, "Passed!" );
+	ok( c.intersectsBox(box) === true, "Passed!" );
 	ok( c.intersectBox(box).distanceTo( new THREE.Vector3( 1, 0, 0 ) ) < TOL, "Passed!" );
 
 	var d = new THREE.Ray( new THREE.Vector3( 0, 2, 1 ), new THREE.Vector3( 0, -1, -1).normalize() );
 	//tilted ray should intersect box at 0,1,0
-	ok( d.isIntersectionBox(box) === true, "Passed!" );
-	ok( d.intersectBox(box).distanceTo( new THREE.Vector3( 0, 1, 0 ) ) < TOL, "Passed!" );	
+	ok( d.intersectsBox(box) === true, "Passed!" );
+	ok( d.intersectBox(box).distanceTo( new THREE.Vector3( 0, 1, 0 ) ) < TOL, "Passed!" );
 
 	var e = new THREE.Ray( new THREE.Vector3( 1, -2, 1 ), new THREE.Vector3( 0, 1, 0).normalize() );
 	//handle case where ray is coplanar with one of the boxes side - box in front of ray
-	ok( e.isIntersectionBox(box) === true, "Passed!" );
-	ok( e.intersectBox(box).distanceTo( new THREE.Vector3( 1, -1, 1 ) ) < TOL, "Passed!" );	
-	
+	ok( e.intersectsBox(box) === true, "Passed!" );
+	ok( e.intersectBox(box).distanceTo( new THREE.Vector3( 1, -1, 1 ) ) < TOL, "Passed!" );
+
 	var f = new THREE.Ray( new THREE.Vector3( 1, -2, 0 ), new THREE.Vector3( 0, -1, 0).normalize() );
 	//handle case where ray is coplanar with one of the boxes side - box behind ray
-	ok( f.isIntersectionBox(box) === false, "Passed!" );
-	ok( f.intersectBox(box) == null, "Passed!" );		
-	
-});
-
+	ok( f.intersectsBox(box) === false, "Passed!" );
+	ok( f.intersectBox(box) == null, "Passed!" );
 
+});

+ 3 - 2
utils/build/build.py

@@ -62,8 +62,9 @@ def main(argv=None):
 				if filename.endswith(".glsl"):
 					tmp.write(u'THREE.ShaderChunk[ \'' + os.path.splitext(os.path.basename(filename))[0] + u'\'] = "')
 					text = f.read()
-					text = re.sub(r"\t*//.*\n", "", text) # strip comments
-					text = text.replace('\n','\\n') # line breaks to \n
+					text = re.sub(r"\t*//.*\n", "", text) # remove //
+					text = re.sub(r"\t*\/\*[\s\S]*\*\/\n", "", text) # remove /* */
+					text = re.sub(r"\n+", '\\\\n', text) # line breaks to \n
 					tmp.write(text)
 					tmp.write(u'";\n\n')
 				else:

+ 8 - 1
utils/build/includes/common.json

@@ -75,6 +75,7 @@
 	"src/materials/MeshBasicMaterial.js",
 	"src/materials/MeshLambertMaterial.js",
 	"src/materials/MeshPhongMaterial.js",
+	"src/materials/MeshStandardMaterial.js",
 	"src/materials/MeshDepthMaterial.js",
 	"src/materials/MeshNormalMaterial.js",
 	"src/materials/MultiMaterial.js",
@@ -124,18 +125,20 @@
 	"src/renderers/shaders/ShaderChunk/envmap_fragment.glsl",
 	"src/renderers/shaders/ShaderChunk/envmap_pars_fragment.glsl",
 	"src/renderers/shaders/ShaderChunk/envmap_pars_vertex.glsl",
+	"src/renderers/shaders/ShaderChunk/envmap_standard_fragment.glsl",
 	"src/renderers/shaders/ShaderChunk/envmap_vertex.glsl",
 	"src/renderers/shaders/ShaderChunk/fog_fragment.glsl",
 	"src/renderers/shaders/ShaderChunk/fog_pars_fragment.glsl",
 	"src/renderers/shaders/ShaderChunk/hemilight_fragment.glsl",
 	"src/renderers/shaders/ShaderChunk/lightmap_fragment.glsl",
 	"src/renderers/shaders/ShaderChunk/lightmap_pars_fragment.glsl",
-	"src/renderers/shaders/ShaderChunk/lights_lambert_pars_vertex.glsl",
+	"src/renderers/shaders/ShaderChunk/lights_pars.glsl",
 	"src/renderers/shaders/ShaderChunk/lights_lambert_vertex.glsl",
 	"src/renderers/shaders/ShaderChunk/lights_phong_fragment.glsl",
 	"src/renderers/shaders/ShaderChunk/lights_phong_pars_fragment.glsl",
 	"src/renderers/shaders/ShaderChunk/lights_phong_pars_vertex.glsl",
 	"src/renderers/shaders/ShaderChunk/lights_phong_vertex.glsl",
+	"src/renderers/shaders/ShaderChunk/lights_standard_fragment.glsl",
 	"src/renderers/shaders/ShaderChunk/linear_to_gamma_fragment.glsl",
 	"src/renderers/shaders/ShaderChunk/logdepthbuf_fragment.glsl",
 	"src/renderers/shaders/ShaderChunk/logdepthbuf_pars_fragment.glsl",
@@ -145,12 +148,16 @@
 	"src/renderers/shaders/ShaderChunk/map_pars_fragment.glsl",
 	"src/renderers/shaders/ShaderChunk/map_particle_fragment.glsl",
 	"src/renderers/shaders/ShaderChunk/map_particle_pars_fragment.glsl",
+	"src/renderers/shaders/ShaderChunk/metalnessmap_fragment.glsl",
+	"src/renderers/shaders/ShaderChunk/metalnessmap_pars_fragment.glsl",
 	"src/renderers/shaders/ShaderChunk/morphnormal_vertex.glsl",
 	"src/renderers/shaders/ShaderChunk/morphtarget_pars_vertex.glsl",
 	"src/renderers/shaders/ShaderChunk/morphtarget_vertex.glsl",
 	"src/renderers/shaders/ShaderChunk/normal_phong_fragment.glsl",
 	"src/renderers/shaders/ShaderChunk/normalmap_pars_fragment.glsl",
 	"src/renderers/shaders/ShaderChunk/project_vertex.glsl",
+	"src/renderers/shaders/ShaderChunk/roughnessmap_fragment.glsl",
+	"src/renderers/shaders/ShaderChunk/roughnessmap_pars_fragment.glsl",
 	"src/renderers/shaders/ShaderChunk/shadowmap_fragment.glsl",
 	"src/renderers/shaders/ShaderChunk/shadowmap_pars_fragment.glsl",
 	"src/renderers/shaders/ShaderChunk/shadowmap_pars_vertex.glsl",

+ 0 - 2
utils/exporters/blender/addons/io_three/constants.py

@@ -272,7 +272,6 @@ SCL = 'scl'
 TIME = 'time'
 KEYS = 'keys'
 
-AMBIENT = 'ambient'
 COLOR = 'color'
 EMISSIVE = 'emissive'
 SPECULAR = 'specular'
@@ -352,7 +351,6 @@ BOTTOM = 'bottom'
 
 SHADING = 'shading'
 COLOR_DIFFUSE = 'colorDiffuse'
-COLOR_AMBIENT = 'colorAmbient'
 COLOR_EMISSIVE = 'colorEmissive'
 COLOR_SPECULAR = 'colorSpecular'
 DBG_NAME = 'DbgName'

+ 0 - 16
utils/exporters/blender/addons/io_three/exporter/api/material.py

@@ -30,22 +30,6 @@ def _material(func):
     return inner
 
 
-@_material
-def ambient_color(material):
-    """
-
-    :param material:
-    :return: rgb value
-    :rtype: tuple
-
-    """
-    logger.debug("material.ambient_color(%s)", material)
-    diffuse = diffuse_color(material)
-    return (material.ambient * diffuse[0],
-            material.ambient * diffuse[1],
-            material.ambient * diffuse[2])
-
-
 @_material
 def blending(material):
     """

+ 0 - 1
utils/exporters/blender/addons/io_three/exporter/api/mesh.py

@@ -527,7 +527,6 @@ def materials(mesh, options):
 
         logger.info("Compiling attributes for %s", mat.name)
         attributes = {
-            constants.COLOR_AMBIENT: material.ambient_color(mat),
             constants.COLOR_EMISSIVE: material.emissive_color(mat),
             constants.SHADING: material.shading(mat),
             constants.OPACITY: material.opacity(mat),

+ 0 - 3
utils/exporters/blender/addons/io_three/exporter/material.py

@@ -32,9 +32,6 @@ class Material(base_classes.BaseNode):
         self[constants.COLOR] = utilities.rgb2int(diffuse)
 
         if self[constants.TYPE] != constants.THREE_BASIC:
-            ambient = api.material.ambient_color(self.node)
-            self[constants.AMBIENT] = utilities.rgb2int(ambient)
-
             emissive = api.material.emissive_color(self.node)
             self[constants.EMISSIVE] = utilities.rgb2int(emissive)
 

Niektóre pliki nie zostały wyświetlone z powodu dużej ilości zmienionych plików