Browse Source

Merge pull request #2 from mrdoob/dev

Merge with main repo
Rik Cabanier 4 years ago
parent
commit
1418c2ba17
64 changed files with 1518 additions and 683 deletions
  1. 1 1
      .github/CONTRIBUTING.md
  2. 27 14
      build/three.js
  3. 0 0
      build/three.min.js
  4. 33 16
      build/three.module.js
  5. 4 4
      docs/api/en/math/MathUtils.html
  6. 29 0
      docs/api/en/objects/InstancedMesh.html
  7. 5 1
      docs/api/en/renderers/WebGLRenderer.html
  8. 5 5
      docs/api/zh/math/MathUtils.html
  9. 29 0
      docs/api/zh/objects/InstancedMesh.html
  10. 5 1
      docs/api/zh/renderers/WebGLRenderer.html
  11. 0 1
      docs/examples/en/exporters/GLTFExporter.html
  12. 0 1
      docs/examples/zh/exporters/GLTFExporter.html
  13. 1 0
      examples/files.json
  14. 0 23
      examples/js/exporters/GLTFExporter.js
  15. 2 2
      examples/js/loaders/FBXLoader.js
  16. 3 0
      examples/js/loaders/GLTFLoader.js
  17. 14 0
      examples/js/loaders/SVGLoader.js
  18. 75 41
      examples/js/modifiers/EdgeSplitModifier.js
  19. 13 3
      examples/js/modifiers/SubdivisionModifier.js
  20. 169 129
      examples/js/modifiers/TessellateModifier.js
  21. 0 23
      examples/jsm/exporters/GLTFExporter.js
  22. 86 14
      examples/jsm/geometries/RoundedBoxBufferGeometry.js
  23. 2 2
      examples/jsm/loaders/FBXLoader.js
  24. 3 0
      examples/jsm/loaders/GLTFLoader.js
  25. 48 37
      examples/jsm/loaders/KTX2Loader.js
  26. 1 0
      examples/jsm/loaders/LottieLoader.js
  27. 14 0
      examples/jsm/loaders/SVGLoader.js
  28. 14 0
      examples/jsm/loaders/XYZLoader.d.ts
  29. 100 0
      examples/jsm/loaders/XYZLoader.js
  30. 14 14
      examples/jsm/modifiers/CurveModifier.d.ts
  31. 1 0
      examples/jsm/modifiers/CurveModifier.js
  32. 84 44
      examples/jsm/modifiers/EdgeSplitModifier.js
  33. 1 1
      examples/jsm/modifiers/SubdivisionModifier.d.ts
  34. 14 3
      examples/jsm/modifiers/SubdivisionModifier.js
  35. 7 4
      examples/jsm/modifiers/TessellateModifier.d.ts
  36. 172 130
      examples/jsm/modifiers/TessellateModifier.js
  37. 5 0
      examples/jsm/nodes/materials/NodeMaterial.js
  38. 95 0
      examples/jsm/renderers/webgpu/ShaderLib.js
  39. 5 97
      examples/jsm/renderers/webgpu/WebGPURenderPipelines.js
  40. 9 1
      examples/jsm/renderers/webgpu/WebGPURenderer.js
  41. 8 0
      examples/models/svg/zero-radius.svg
  42. 203 0
      examples/models/xyz/helix_201.xyz
  43. BIN
      examples/screenshots/webgl_gpgpu_birds_gltf.jpg
  44. BIN
      examples/screenshots/webgl_loader_xyz.jpg
  45. 3 1
      examples/webgl_gpgpu_birds.html
  46. 8 6
      examples/webgl_gpgpu_birds_gltf.html
  47. 2 2
      examples/webgl_loader_svg.html
  48. 1 1
      examples/webgl_loader_texture_lottie.html
  49. 94 0
      examples/webgl_loader_xyz.html
  50. 14 19
      examples/webgl_modifier_curve.html
  51. 13 19
      examples/webgl_modifier_curve_instanced.html
  52. 2 6
      examples/webgl_modifier_tessellation.html
  53. 2 2
      src/core/Geometry.d.ts
  54. 1 2
      src/core/Geometry.js
  55. 7 0
      src/math/Matrix3.d.ts
  56. 9 0
      src/math/Matrix4.d.ts
  57. 3 0
      src/math/Vector2.d.ts
  58. 4 0
      src/math/Vector3.d.ts
  59. 3 0
      src/math/Vector4.d.ts
  60. 3 1
      src/objects/InstancedMesh.d.ts
  61. 14 8
      src/objects/InstancedMesh.js
  62. 9 1
      src/renderers/webgl/WebGLBindingStates.js
  63. 24 3
      test/unit/src/core/Object3D.tests.js
  64. 1 0
      utils/modularize.js

+ 1 - 1
.github/CONTRIBUTING.md

@@ -28,7 +28,7 @@ As per the npm standard, ‘start’ is the place to begin the package.
 
 This script will start a local server similar to [threejs.org](https://threejs.org/), but instead will be hosted on your local machine. Browse to http://localhost:8080/ to check it out. It also automatically creates the ‘build/three.js’ and ‘build/three.module.js’ scripts anytime there is a change within your three.js directory.
 
-The next most important script runs all the appropriate testing.
+The next most important script runs all the appropriate testing. The E-2-E testing is intended to be run by github actions.
 
     npm test
 

+ 27 - 14
build/three.js

@@ -11106,7 +11106,7 @@
 		function needsUpdate(geometry, index) {
 			var cachedAttributes = currentState.attributes;
 			var geometryAttributes = geometry.attributes;
-			if (Object.keys(cachedAttributes).length !== Object.keys(geometryAttributes).length) return true;
+			var attributesNum = 0;
 
 			for (var key in geometryAttributes) {
 				var cachedAttribute = cachedAttributes[key];
@@ -11114,8 +11114,10 @@
 				if (cachedAttribute === undefined) return true;
 				if (cachedAttribute.attribute !== geometryAttribute) return true;
 				if (cachedAttribute.data !== geometryAttribute.data) return true;
+				attributesNum++;
 			}
 
+			if (currentState.attributesNum !== attributesNum) return true;
 			if (currentState.index !== index) return true;
 			return false;
 		}
@@ -11123,6 +11125,7 @@
 		function saveCache(geometry, index) {
 			var cache = {};
 			var attributes = geometry.attributes;
+			var attributesNum = 0;
 
 			for (var key in attributes) {
 				var attribute = attributes[key];
@@ -11134,9 +11137,11 @@
 				}
 
 				cache[key] = data;
+				attributesNum++;
 			}
 
 			currentState.attributes = cache;
+			currentState.attributesNum = attributesNum;
 			currentState.index = index;
 		}
 
@@ -18327,7 +18332,7 @@
 					var bones = skeleton.bones;
 
 					if (capabilities.floatVertexTextures) {
-						if (skeleton.boneTexture === undefined) {
+						if (skeleton.boneTexture === null) {
 							// layout (1 matrix = 4 pixels)
 							//			RGBA RGBA RGBA RGBA (=> column1, column2, column3, column4)
 							//	with	8x8	pixel texture max	 16 bones * 4 pixels =	(8 * 8)
@@ -19435,6 +19440,9 @@
 		this.uuid = MathUtils.generateUUID();
 		this.bones = bones.slice(0);
 		this.boneInverses = boneInverses;
+		this.boneMatrices = null;
+		this.boneTexture = null;
+		this.boneTextureSize = 0;
 		this.frame = -1;
 		this.init();
 	}
@@ -19514,7 +19522,7 @@
 				_offsetMatrix.toArray(boneMatrices, i * 16);
 			}
 
-			if (boneTexture !== undefined) {
+			if (boneTexture !== null) {
 				boneTexture.needsUpdate = true;
 			}
 		},
@@ -19533,9 +19541,9 @@
 			return undefined;
 		},
 		dispose: function dispose() {
-			if (this.boneTexture) {
+			if (this.boneTexture !== null) {
 				this.boneTexture.dispose();
-				this.boneTexture = undefined;
+				this.boneTexture = null;
 			}
 		},
 		fromJSON: function fromJSON(json, bones) {
@@ -19607,12 +19615,8 @@
 			this.count = source.count;
 			return this;
 		},
-		setColorAt: function setColorAt(index, color) {
-			if (this.instanceColor === null) {
-				this.instanceColor = new BufferAttribute(new Float32Array(this.count * 3), 3);
-			}
-
-			color.toArray(this.instanceColor.array, index * 3);
+		getColorAt: function getColorAt(index, color) {
+			color.fromArray(this.instanceColor.array, index * 3);
 		},
 		getMatrixAt: function getMatrixAt(index, matrix) {
 			matrix.fromArray(this.instanceMatrix.array, index * 16);
@@ -19646,6 +19650,13 @@
 				_instanceIntersects.length = 0;
 			}
 		},
+		setColorAt: function setColorAt(index, color) {
+			if (this.instanceColor === null) {
+				this.instanceColor = new BufferAttribute(new Float32Array(this.count * 3), 3);
+			}
+
+			color.toArray(this.instanceColor.array, index * 3);
+		},
 		setMatrixAt: function setMatrixAt(index, matrix) {
 			matrix.toArray(this.instanceMatrix.array, index * 16);
 		},
@@ -20718,13 +20729,15 @@
 		 * Duplicated vertices are removed
 		 * and faces' vertices are updated.
 		 */
-		mergeVertices: function mergeVertices() {
+		mergeVertices: function mergeVertices(precisionPoints) {
+			if (precisionPoints === void 0) {
+				precisionPoints = 4;
+			}
+
 			var verticesMap = {}; // Hashmap for looking up vertices by position coordinates (and making sure they are unique)
 
 			var unique = [],
 					changes = [];
-			var precisionPoints = 4; // number of decimal points, e.g. 4 for epsilon of 0.0001
-
 			var precision = Math.pow(10, precisionPoints);
 
 			for (var i = 0, il = this.vertices.length; i < il; i++) {

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


+ 33 - 16
build/three.module.js

@@ -12763,7 +12763,7 @@ function WebGLAttributes( gl, capabilities ) {
 
 		if ( attribute.isGLBufferAttribute ) {
 
-			var cached = buffers.get( attribute );
+			const cached = buffers.get( attribute );
 
 			if ( ! cached || cached.version < attribute.version ) {
 
@@ -14189,7 +14189,7 @@ function WebGLBindingStates( gl, extensions, attributes, capabilities ) {
 		const cachedAttributes = currentState.attributes;
 		const geometryAttributes = geometry.attributes;
 
-		if ( Object.keys( cachedAttributes ).length !== Object.keys( geometryAttributes ).length ) return true;
+		let attributesNum = 0;
 
 		for ( const key in geometryAttributes ) {
 
@@ -14202,8 +14202,12 @@ function WebGLBindingStates( gl, extensions, attributes, capabilities ) {
 
 			if ( cachedAttribute.data !== geometryAttribute.data ) return true;
 
+			attributesNum ++;
+
 		}
 
+		if ( currentState.attributesNum !== attributesNum ) return true;
+
 		if ( currentState.index !== index ) return true;
 
 		return false;
@@ -14214,6 +14218,7 @@ function WebGLBindingStates( gl, extensions, attributes, capabilities ) {
 
 		const cache = {};
 		const attributes = geometry.attributes;
+		let attributesNum = 0;
 
 		for ( const key in attributes ) {
 
@@ -14230,9 +14235,12 @@ function WebGLBindingStates( gl, extensions, attributes, capabilities ) {
 
 			cache[ key ] = data;
 
+			attributesNum ++;
+
 		}
 
 		currentState.attributes = cache;
+		currentState.attributesNum = attributesNum;
 
 		currentState.index = index;
 
@@ -24687,7 +24695,7 @@ function WebGLRenderer( parameters ) {
 
 				if ( capabilities.floatVertexTextures ) {
 
-					if ( skeleton.boneTexture === undefined ) {
+					if ( skeleton.boneTexture === null ) {
 
 						// layout (1 matrix = 4 pixels)
 						//      RGBA RGBA RGBA RGBA (=> column1, column2, column3, column4)
@@ -26229,6 +26237,10 @@ function Skeleton( bones = [], boneInverses = [] ) {
 
 	this.bones = bones.slice( 0 );
 	this.boneInverses = boneInverses;
+	this.boneMatrices = null;
+
+	this.boneTexture = null;
+	this.boneTextureSize = 0;
 
 	this.frame = - 1;
 
@@ -26356,7 +26368,7 @@ Object.assign( Skeleton.prototype, {
 
 		}
 
-		if ( boneTexture !== undefined ) {
+		if ( boneTexture !== null ) {
 
 			boneTexture.needsUpdate = true;
 
@@ -26390,11 +26402,11 @@ Object.assign( Skeleton.prototype, {
 
 	dispose: function ( ) {
 
-		if ( this.boneTexture ) {
+		if ( this.boneTexture !== null ) {
 
 			this.boneTexture.dispose();
 
-			this.boneTexture = undefined;
+			this.boneTexture = null;
 
 		}
 
@@ -26497,15 +26509,9 @@ InstancedMesh.prototype = Object.assign( Object.create( Mesh.prototype ), {
 
 	},
 
-	setColorAt: function ( index, color ) {
-
-		if ( this.instanceColor === null ) {
-
-			this.instanceColor = new BufferAttribute( new Float32Array( this.count * 3 ), 3 );
+	getColorAt: function ( index, color ) {
 
-		}
-
-		color.toArray( this.instanceColor.array, index * 3 );
+		color.fromArray( this.instanceColor.array, index * 3 );
 
 	},
 
@@ -26556,6 +26562,18 @@ InstancedMesh.prototype = Object.assign( Object.create( Mesh.prototype ), {
 
 	},
 
+	setColorAt: function ( index, color ) {
+
+		if ( this.instanceColor === null ) {
+
+			this.instanceColor = new BufferAttribute( new Float32Array( this.count * 3 ), 3 );
+
+		}
+
+		color.toArray( this.instanceColor.array, index * 3 );
+
+	},
+
 	setMatrixAt: function ( index, matrix ) {
 
 		matrix.toArray( this.instanceMatrix.array, index * 16 );
@@ -28108,12 +28126,11 @@ Geometry.prototype = Object.assign( Object.create( EventDispatcher.prototype ),
 	 * and faces' vertices are updated.
 	 */
 
-	mergeVertices: function () {
+	mergeVertices: function ( precisionPoints = 4 ) {
 
 		const verticesMap = {}; // Hashmap for looking up vertices by position coordinates (and making sure they are unique)
 		const unique = [], changes = [];
 
-		const precisionPoints = 4; // number of decimal points, e.g. 4 for epsilon of 0.0001
 		const precision = Math.pow( 10, precisionPoints );
 
 		for ( let i = 0, il = this.vertices.length; i < il; i ++ ) {

+ 4 - 4
docs/api/en/math/MathUtils.html

@@ -74,16 +74,16 @@
 		<p>Converts radians to degrees.</p>
 
 		<h3>[method:Float randFloat]( [param:Float low], [param:Float high] )</h3>
-		<p>Random float in the interval [page:Float low] to [page:Float high].</p>
+		<p>Random float in the interval [[page:Float low], [page:Float high]].</p>
 
 		<h3>[method:Float randFloatSpread]( [param:Float range] )</h3>
-		<p>Random float in the interval *- [page:Float range] / 2* to *[page:Float range] / 2*.</p>
+		<p>Random float in the interval [- [page:Float range] / 2, [page:Float range] / 2].</p>
 
 		<h3>[method:Integer randInt]( [param:Integer low], [param:Integer high] )</h3>
-		<p>Random integer in the interval [page:Float low] to [page:Float high].</p>
+		<p>Random integer in the interval [[page:Float low], [page:Float high]].</p>
 
 		<h3>[method:Float seededRandom]( [param:Integer seed] )</h3>
-		<p>Deterministic pseudo-random float in the interval [ 0, 1 ]. The integer [page:Integer seed] is optional.</p>
+		<p>Deterministic pseudo-random float in the interval [0, 1]. The integer [page:Integer seed] is optional.</p>
 
 		<h3>[method:Float smoothstep]( [param:Float x], [param:Float min], [param:Float max] )</h3>
 		<p>

+ 29 - 0
docs/api/en/objects/InstancedMesh.html

@@ -51,6 +51,12 @@
 			If you need more instances than the original count value, you have to create a new [name].
 		</p>
 
+		<h3>[property:BufferAttribute instanceColor]</h3>
+		<p>
+			Represents the colors of all instances. *null* by default.
+			You have to set its [page:BufferAttribute.needsUpdate needsUpdate] flag to true if you modify instanced data via [page:.setColorAt]().
+		</p>
+
 		<h3>[property:BufferAttribute instanceMatrix]</h3>
 		<p>
 			Represents the local transformation of all instances.
@@ -60,6 +66,17 @@
 		<h2>Methods</h2>
 		<p>See the base [page:Mesh] class for common methods.</p>
 
+		<h3>[method:null getColorAt]( [param:Integer index], [param:Color color] )</h3>
+		<p>
+			[page:Integer index]: The index of an instance. Values have to be in the range [0, count].
+		</p>
+		<p>
+			[page:Color color]: This color object will be set to the color of the defined instance.
+		</p>
+		<p>
+			Get the color of the defined instance.
+		</p>
+
 		<h3>[method:null getMatrixAt]( [param:Integer index], [param:Matrix4 matrix] )</h3>
 		<p>
 			[page:Integer index]: The index of an instance. Values have to be in the range [0, count].
@@ -71,6 +88,18 @@
 			Get the local transformation matrix of the defined instance.
 		</p>
 
+		<h3>[method:null setColorAt]( [param:Integer index], [param:Color color] )</h3>
+		<p>
+			[page:Integer index]: The index of an instance. Values have to be in the range [0, count].
+		</p>
+		<p>
+			[page:Color color]: The color of a single instance.
+		</p>
+		<p>
+			Sets the given color to the defined instance.
+			Make sure you set [page:.instanceColor][page:BufferAttribute.needsUpdate .needsUpdate] to true after updating all the colors.
+		</p>
+
 		<h3>[method:null setMatrixAt]( [param:Integer index], [param:Matrix4 matrix] )</h3>
 		<p>
 			[page:Integer index]: The index of an instance. Values have to be in the range [0, count].

+ 5 - 1
docs/api/en/renderers/WebGLRenderer.html

@@ -191,9 +191,13 @@
 			</li>
 		</ul>
 		</p>
-		<p>By default these data are reset at each render calls, but when using the composer or mirrors it can be preferred to reset them with a custom pattern :
+		<p>By default these data are reset at each render call but when having multiple render passes per frame (e.g. when using post processing) it can be preferred to reset with a custom pattern.
+			First, set *autoReset* to *false*.
 		<code>
 		renderer.info.autoReset = false;
+		</code>
+		Call *reset()* whenever you have finished to render a single frame.
+		<code>
 		renderer.info.reset();
 		</code>
 		</p>

+ 5 - 5
docs/api/zh/math/MathUtils.html

@@ -44,7 +44,7 @@
 		<p>
 		[page:Float x] - 起始点。 <br />
 		[page:Float y] - 终点。 <br />
-		[page:Float t] - 闭区间[0,1]内的插值因子。<br><br />
+		[page:Float t] - 闭区间 [0,1] 内的插值因子。<br><br />
 
 		返回给定区间的线性插值[link:https://en.wikipedia.org/wiki/Linear_interpolation linearly interpolated]结果 - [page:Float t] = 0 将会返回 [page:Float x]
 		如果 [page:Float t] = 1 将会返回 [page:Float y].
@@ -71,16 +71,16 @@
 		<p>将弧度转换为角度。</p>
 
 		<h3>[method:Float randFloat]( [param:Float low], [param:Float high] )</h3>
-		<p>在区间[page:Float low] 到 [page:Float high]随机一个浮点数。</p>
+		<p>在区间 [[page:Float low], [page:Float high]] 内随机一个浮点数。</p>
 
 		<h3>[method:Float randFloatSpread]( [param:Float range] )</h3>
-		<p>在区间*- [page:Float range] / 2* 到 *[page:Float range] / 2*随机一个浮点数。</p>
+		<p>在区间 [- [page:Float range] / 2, [page:Float range] / 2] 内随机一个浮点数。</p>
 
 		<h3>[method:Integer randInt]( [param:Integer low], [param:Integer high] )</h3>
-		<p>在区间[page:Float low] 到 [page:Float high]随机一个整数。</p>
+		<p>在区间 [[page:Float low], [page:Float high]] 内随机一个整数。</p>
 
 		<h3>[method:Float seededRandom]( [param:Integer seed] )</h3>
-		<p>Deterministic pseudo-random float in the interval [ 0, 1 ]. The integer [page:Integer seed] is optional.</p>
+		<p>在区间 [0, 1] 中生成确定性的伪随机浮点数。 整数种子是可选的。</p>
 
 		<h3>[method:Float smoothstep]( [param:Float x], [param:Float min], [param:Float max] )</h3>
 		<p>

+ 29 - 0
docs/api/zh/objects/InstancedMesh.html

@@ -49,6 +49,12 @@
 			如果你需要比原先的数量更多的实例数量,你需要创建一个新的[name]。
 		</p>
 
+		<h3>[property:BufferAttribute instanceColor]</h3>
+		<p>
+			Represents the colors of all instances. *null* by default.
+			You have to set its [page:BufferAttribute.needsUpdate needsUpdate] flag to true if you modify instanced data via [page:.setColorAt]().
+		</p>
+
 		<h3>[property:BufferAttribute instanceMatrix]</h3>
 		<p>
 			表示所有实例的本地变换。
@@ -59,6 +65,17 @@
 		<h2>方法</h2>
 		<p>See the base [page:Mesh] class for common methods.</p>
 
+		<h3>[method:null getColorAt]( [param:Integer index], [param:Color color] )</h3>
+		<p>
+			[page:Integer index]: The index of an instance. Values have to be in the range [0, count].
+		</p>
+		<p>
+			[page:Color color]: This color object will be set to the color of the defined instance.
+		</p>
+		<p>
+			Get the color of the defined instance.
+		</p>
+
 		<h3>[method:null getMatrixAt]( [param:Integer index], [param:Matrix4 matrix] )</h3>
 		<p>
 			[page:Integer index]: 实例的索引。值必须在 [0, count] 区间。
@@ -70,6 +87,18 @@
 			获得已定义实例的本地变换矩阵。
 		</p>
 
+		<h3>[method:null setColorAt]( [param:Integer index], [param:Color color] )</h3>
+		<p>
+			[page:Integer index]: The index of an instance. Values have to be in the range [0, count].
+		</p>
+		<p>
+			[page:Color color]: The color of a single instance.
+		</p>
+		<p>
+			Sets the given color to the defined instance.
+			Make sure you set [page:.instanceColor][page:BufferAttribute.needsUpdate .needsUpdate] to true after updating all the colors.
+		</p>
+
 		<h3>[method:null setMatrixAt]( [param:Integer index], [param:Matrix4 matrix] )</h3>
 		<p>
 			[page:Integer index]: 实例的索引。值必须在 [0, count] 区间。

+ 5 - 1
docs/api/zh/renderers/WebGLRenderer.html

@@ -170,9 +170,13 @@
 			</li>
 		</ul>
 		</p>
-		<p>默认情况下,每次调用渲染时这些数据都会重置。 但当时用一个或多个镜像时,最好使用自定义模式重置它们:
+		<p>By default these data are reset at each render call but when having multiple render passes per frame (e.g. when using post processing) it can be preferred to reset with a custom pattern.
+			First, set *autoReset* to *false*.
 		<code>
 		renderer.info.autoReset = false;
+		</code>
+		Call *reset()* whenever you have finished to render a single frame.
+		<code>
 		renderer.info.reset();
 		</code>
 		</p>

+ 0 - 1
docs/examples/en/exporters/GLTFExporter.html

@@ -100,7 +100,6 @@
 			<li>maxTextureSize - int. Restricts the image maximum size (both width and height) to the given value. This option works only if embedImages is true. Default is Infinity.</li>
 			<li>animations - Array<[page:AnimationClip AnimationClip]>. List of animations to be included in the export.</li>
 			<li>forceIndices - bool. Generate indices for non-index geometry and export with them. Default is false.</li>
-			<li>forcePowerOfTwoTextures - bool. Export with images resized to POT size. This option works only if embedImages is true. Default is false.</li>
 			<li>includeCustomExtensions - bool. Export custom glTF extensions defined on an object's <em>userData.gltfExtensions</em> property. Default is false.</li>
 		</ul>
 		</p>

+ 0 - 1
docs/examples/zh/exporters/GLTFExporter.html

@@ -100,7 +100,6 @@
 			<li>maxTextureSize - int. Restricts the image maximum size (both width and height) to the given value. This option works only if embedImages is true. Default is Infinity.</li>
 			<li>animations - Array<[page:AnimationClip AnimationClip]>. List of animations to be included in the export.</li>
 			<li>forceIndices - bool. Generate indices for non-index geometry and export with them. Default is false.</li>
-			<li>forcePowerOfTwoTextures - bool. Export with images resized to POT size. This option works only if embedImages is true. Default is false.</li>
 			<li>includeCustomExtensions - bool. Export custom glTF extensions defined on an object's <em>userData.gltfExtensions</em> property. Default is false.</li>
 		</ul>
 		</p>

+ 1 - 0
examples/files.json

@@ -129,6 +129,7 @@
 		"webgl_loader_vrml",
 		"webgl_loader_vtk",
 		"webgl_loader_x",
+		"webgl_loader_xyz",
 		"webgl_lod",
 		"webgl_marchingcubes",
 		"webgl_materials",

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

@@ -76,7 +76,6 @@ THREE.GLTFExporter.prototype = {
 			embedImages: true,
 			maxTextureSize: Infinity,
 			animations: [],
-			forcePowerOfTwoTextures: false,
 			includeCustomExtensions: false
 		};
 
@@ -224,19 +223,6 @@ THREE.GLTFExporter.prototype = {
 
 		}
 
-		/**
-		 * Checks if image size is POT.
-		 *
-		 * @param {Image} image The image to be checked.
-		 * @returns {Boolean} Returns true if image size is POT.
-		 *
-		 */
-		function isPowerOfTwo( image ) {
-
-			return THREE.MathUtils.isPowerOfTwo( image.width ) && THREE.MathUtils.isPowerOfTwo( image.height );
-
-		}
-
 		/**
 		 * Checks if normal attribute values are normalized.
 		 *
@@ -781,15 +767,6 @@ THREE.GLTFExporter.prototype = {
 				canvas.width = Math.min( image.width, options.maxTextureSize );
 				canvas.height = Math.min( image.height, options.maxTextureSize );
 
-				if ( options.forcePowerOfTwoTextures && ! isPowerOfTwo( canvas ) ) {
-
-					console.warn( 'GLTFExporter: Resized non-power-of-two image.', image );
-
-					canvas.width = THREE.MathUtils.floorPowerOfTwo( canvas.width );
-					canvas.height = THREE.MathUtils.floorPowerOfTwo( canvas.height );
-
-				}
-
 				var ctx = canvas.getContext( '2d' );
 
 				if ( flipY === true ) {

+ 2 - 2
examples/js/loaders/FBXLoader.js

@@ -495,7 +495,7 @@ THREE.FBXLoader = ( function () {
 
 				parameters.color = new THREE.Color().fromArray( materialNode.Diffuse.value );
 
-			} else if ( materialNode.DiffuseColor && materialNode.DiffuseColor.type === 'Color' ) {
+			} else if ( materialNode.DiffuseColor && ( materialNode.DiffuseColor.type === 'Color' || materialNode.DiffuseColor.type === 'ColorRGB' ) ) {
 
 				// The blender exporter exports diffuse here instead of in materialNode.Diffuse
 				parameters.color = new THREE.Color().fromArray( materialNode.DiffuseColor.value );
@@ -512,7 +512,7 @@ THREE.FBXLoader = ( function () {
 
 				parameters.emissive = new THREE.Color().fromArray( materialNode.Emissive.value );
 
-			} else if ( materialNode.EmissiveColor && materialNode.EmissiveColor.type === 'Color' ) {
+			} else if ( materialNode.EmissiveColor && ( materialNode.EmissiveColor.type === 'Color' || materialNode.EmissiveColor.type === 'ColorRGB' ) ) {
 
 				// The blender exporter exports emissive color here instead of in materialNode.Emissive
 				parameters.emissive = new THREE.Color().fromArray( materialNode.EmissiveColor.value );

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

@@ -3191,6 +3191,7 @@ THREE.GLTFLoader = ( function () {
 
 		var parser = this;
 		var json = this.json;
+		var extensions = this.extensions;
 
 		var meshDef = json.meshes[ meshIndex ];
 		var primitives = meshDef.primitives;
@@ -3287,6 +3288,8 @@ THREE.GLTFLoader = ( function () {
 
 				assignExtrasToUserData( mesh, meshDef );
 
+				if ( primitive.extensions ) addUnknownExtensionsToUserData( extensions, mesh, primitive );
+
 				parser.assignFinalMaterial( mesh );
 
 				meshes.push( mesh );

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

@@ -387,6 +387,9 @@ THREE.SVGLoader.prototype = Object.assign( Object.create( THREE.Loader.prototype
 
 						for ( var j = 0, jl = numbers.length; j < jl; j += 7 ) {
 
+							// skip command if start point == end point
+							if ( numbers[ j + 5 ] == point.x && numbers[ j + 6 ] == point.y ) continue;
+
 							var start = point.clone();
 							point.x = numbers[ j + 5 ];
 							point.y = numbers[ j + 6 ];
@@ -576,6 +579,9 @@ THREE.SVGLoader.prototype = Object.assign( Object.create( THREE.Loader.prototype
 
 						for ( var j = 0, jl = numbers.length; j < jl; j += 7 ) {
 
+							// skip command if no displacement
+							if ( numbers[ j + 5 ] == 0 && numbers[ j + 6 ] == 0 ) continue;
+
 							var start = point.clone();
 							point.x += numbers[ j + 5 ];
 							point.y += numbers[ j + 6 ];
@@ -660,6 +666,14 @@ THREE.SVGLoader.prototype = Object.assign( Object.create( THREE.Loader.prototype
 
 		function parseArcCommand( path, rx, ry, x_axis_rotation, large_arc_flag, sweep_flag, start, end ) {
 
+			if ( rx == 0 || ry == 0 ) {
+
+				// draw a line if either of the radii == 0
+				path.lineTo( end.x, end.y );
+				return;
+
+			}
+
 			x_axis_rotation = x_axis_rotation * Math.PI / 180;
 
 			// Ensure radii are positive

+ 75 - 41
examples/js/modifiers/EdgeSplitModifier.js

@@ -1,22 +1,23 @@
 
 THREE.EdgeSplitModifier = function () {
 
-	const A = new THREE.Vector3();
-	const B = new THREE.Vector3();
-	const C = new THREE.Vector3();
+	var A = new THREE.Vector3();
+	var B = new THREE.Vector3();
+	var C = new THREE.Vector3();
 
-	let positions, normals;
-	let indexes;
-	let pointToIndexMap, splitIndexes;
+	var positions, normals;
+	var indexes;
+	var pointToIndexMap, splitIndexes;
 
 
 	function computeNormals() {
 
 		normals = new Float32Array( indexes.length * 3 );
 
-		for ( let i = 0; i < indexes.length; i += 3 ) {
+		for ( var i = 0; i < indexes.length; i += 3 ) {
+
+			var index = indexes[ i ];
 
-			let index = indexes[ i ];
 			A.set(
 				positions[ 3 * index ],
 				positions[ 3 * index + 1 ],
@@ -37,9 +38,9 @@ THREE.EdgeSplitModifier = function () {
 			C.sub( B );
 			A.sub( B );
 
-			const normal = C.cross( A ).normalize();
+			var normal = C.cross( A ).normalize();
 
-			for ( let j = 0; j < 3; j ++ ) {
+			for ( var j = 0; j < 3; j ++ ) {
 
 				normals[ 3 * ( i + j ) ] = normal.x;
 				normals[ 3 * ( i + j ) + 1 ] = normal.y;
@@ -56,13 +57,16 @@ THREE.EdgeSplitModifier = function () {
 
 		pointToIndexMap = Array( positions.length / 3 );
 
-		for ( let i = 0; i < indexes.length; i ++ ) {
+		for ( var i = 0; i < indexes.length; i ++ ) {
+
+			var index = indexes[ i ];
 
-			const index = indexes[ i ];
+			if ( pointToIndexMap[ index ] == null ) {
 
-			if ( pointToIndexMap[ index ] == null )
 				pointToIndexMap[ index ] = [];
 
+			}
+
 			pointToIndexMap[ index ].push( i );
 
 		}
@@ -72,27 +76,29 @@ THREE.EdgeSplitModifier = function () {
 
 	function edgeSplitToGroups( indexes, cutOff, firstIndex ) {
 
-		A.set( normals[ 3 * firstIndex ], normals[ 3 * firstIndex + 1 ], normals[ 3 * firstIndex + 2 ] )
-			.normalize();
+		A.set( normals[ 3 * firstIndex ], normals[ 3 * firstIndex + 1 ], normals[ 3 * firstIndex + 2 ] ).normalize();
 
-		const result = {
+		var result = {
 			splitGroup: [],
 			currentGroup: [ firstIndex ]
 		};
 
-		for ( const j of indexes ) {
+		for ( var j of indexes ) {
 
 			if ( j !== firstIndex ) {
 
-				B.set( normals[ 3 * j ], normals[ 3 * j + 1 ], normals[ 3 * j + 2 ] )
-					.normalize();
+				B.set( normals[ 3 * j ], normals[ 3 * j + 1 ], normals[ 3 * j + 2 ] ).normalize();
+
+				if ( B.dot( A ) < cutOff ) {
 
-				if ( B.dot( A ) < cutOff )
 					result.splitGroup.push( j );
 
-				else
+				} else {
+
 					result.currentGroup.push( j );
 
+				}
+
 			}
 
 		}
@@ -104,27 +110,44 @@ THREE.EdgeSplitModifier = function () {
 
 	function edgeSplit( indexes, cutOff, original = null ) {
 
-		if ( indexes.length === 0 )
-			return;
+		if ( indexes.length === 0 ) return;
+
+		var groupResults = [];
+
+		for ( var index of indexes ) {
 
-		const groupResults = [];
-		for ( const index of indexes )
 			groupResults.push( edgeSplitToGroups( indexes, cutOff, index ) );
 
-		let result = groupResults[ 0 ];
-		for ( const groupResult of groupResults )
-			if ( groupResult.currentGroup.length > result.currentGroup.length )
+		}
+
+		var result = groupResults[ 0 ];
+
+		for ( var groupResult of groupResults ) {
+
+			if ( groupResult.currentGroup.length > result.currentGroup.length ) {
+
 				result = groupResult;
 
-		if ( original != null )
+			}
+
+		}
+
+
+		if ( original != null ) {
+
 			splitIndexes.push( {
 				original: original,
 				indexes: result.currentGroup
 			} );
 
-		if ( result.splitGroup.length )
+		}
+
+		if ( result.splitGroup.length ) {
+
 			edgeSplit( result.splitGroup, cutOff, original || result.currentGroup[ 0 ] );
 
+		}
+
 	}
 
 
@@ -137,44 +160,55 @@ THREE.EdgeSplitModifier = function () {
 		}
 
 
-		if ( geometry.index == null )
+		if ( geometry.index == null ) {
+
+			if ( THREE.BufferGeometryUtils === undefined ) {
+
+			 	throw 'THREE.EdgeSplitModifier relies on THREE.BufferGeometryUtils';
+
+			}
+
 			geometry = THREE.BufferGeometryUtils.mergeVertices( geometry );
 
+		}
 
 		indexes = geometry.index.array;
 		positions = geometry.getAttribute( "position" ).array;
 
 		computeNormals();
-
-
 		mapPositionsToIndexes();
 
 
 		splitIndexes = [];
 
-		for ( const vertexIndexes of pointToIndexMap )
+		for ( var vertexIndexes of pointToIndexMap ) {
+
 			edgeSplit( vertexIndexes, Math.cos( cutOffAngle ) - 0.001 );
 
+		}
 
-		const newPositions = new Float32Array( positions.length + 3 * splitIndexes.length );
+		var newPositions = new Float32Array( positions.length + 3 * splitIndexes.length );
 		newPositions.set( positions );
-		const offset = positions.length;
+		var offset = positions.length;
 
-		const newIndexes = new Uint32Array( indexes.length );
+		var newIndexes = new Uint32Array( indexes.length );
 		newIndexes.set( indexes );
 
-		for ( let i = 0; i < splitIndexes.length; i ++ ) {
+		for ( var i = 0; i < splitIndexes.length; i ++ ) {
 
-			const split = splitIndexes[ i ];
-			const index = indexes[ split.original ];
+			var split = splitIndexes[ i ];
+			var index = indexes[ split.original ];
 
 			newPositions[ offset + 3 * i ] = positions[ 3 * index ];
 			newPositions[ offset + 3 * i + 1 ] = positions[ 3 * index + 1 ];
 			newPositions[ offset + 3 * i + 2 ] = positions[ 3 * index + 2 ];
 
-			for ( const j of split.indexes )
+			for ( var j of split.indexes ) {
+
 				newIndexes[ j ] = offset / 3 + i;
 
+			}
+
 		}
 
 		geometry = new THREE.BufferGeometry();

+ 13 - 3
examples/js/modifiers/SubdivisionModifier.js

@@ -20,7 +20,9 @@ THREE.SubdivisionModifier = function ( subdivisions ) {
 // Applies the "modify" pattern
 THREE.SubdivisionModifier.prototype.modify = function ( geometry ) {
 
-	if ( geometry.isBufferGeometry ) {
+	var isBufferGeometry = geometry.isBufferGeometry;
+
+	if ( isBufferGeometry ) {
 
 		geometry = new THREE.Geometry().fromBufferGeometry( geometry );
 
@@ -30,7 +32,7 @@ THREE.SubdivisionModifier.prototype.modify = function ( geometry ) {
 
 	}
 
-	geometry.mergeVertices();
+	geometry.mergeVertices( 6 );
 
 	var repeats = this.subdivisions;
 
@@ -43,7 +45,15 @@ THREE.SubdivisionModifier.prototype.modify = function ( geometry ) {
 	geometry.computeFaceNormals();
 	geometry.computeVertexNormals();
 
-	return geometry;
+	if ( isBufferGeometry ) {
+
+		return new THREE.BufferGeometry().fromGeometry( geometry );
+
+	} else {
+
+		return geometry;
+
+	}
 
 };
 

+ 169 - 129
examples/js/modifiers/TessellateModifier.js

@@ -1,224 +1,254 @@
 /**
  * Break faces with edges longer than maxEdgeLength
- * - not recursive
  */
 
-THREE.TessellateModifier = function ( maxEdgeLength ) {
+THREE.TessellateModifier = function ( maxEdgeLength = 0.1, maxIterations = 6, maxFaces = Infinity ) {
 
 	this.maxEdgeLength = maxEdgeLength;
+	this.maxIterations = maxIterations;
+	this.maxFaces = maxFaces;
 
 };
 
+// Applies the "modify" pattern
 THREE.TessellateModifier.prototype.modify = function ( geometry ) {
 
-	var edge;
+	const isBufferGeometry = geometry.isBufferGeometry;
 
-	var faces = [];
-	var faceVertexUvs = [];
-	var maxEdgeLengthSquared = this.maxEdgeLength * this.maxEdgeLength;
+	if ( isBufferGeometry ) {
 
-	for ( var i = 0, il = geometry.faceVertexUvs.length; i < il; i ++ ) {
+		geometry = new THREE.Geometry().fromBufferGeometry( geometry );
 
-		faceVertexUvs[ i ] = [];
+	} else {
+
+		geometry = geometry.clone();
 
 	}
 
-	for ( var i = 0, il = geometry.faces.length; i < il; i ++ ) {
+	geometry.mergeVertices( 6 );
 
-		var face = geometry.faces[ i ];
+	let finalized = false;
+	let iteration = 0;
+	const maxEdgeLengthSquared = this.maxEdgeLength * this.maxEdgeLength;
 
-		if ( face instanceof THREE.Face3 ) {
+	let edge;
 
-			var a = face.a;
-			var b = face.b;
-			var c = face.c;
+	while ( ! finalized && iteration < this.maxIterations && geometry.faces.length < this.maxFaces ) {
 
-			var va = geometry.vertices[ a ];
-			var vb = geometry.vertices[ b ];
-			var vc = geometry.vertices[ c ];
+		const faces = [];
+		const faceVertexUvs = [];
 
-			var dab = va.distanceToSquared( vb );
-			var dbc = vb.distanceToSquared( vc );
-			var dac = va.distanceToSquared( vc );
+		finalized = true;
+		iteration ++;
 
-			if ( dab > maxEdgeLengthSquared || dbc > maxEdgeLengthSquared || dac > maxEdgeLengthSquared ) {
+		for ( var i = 0, il = geometry.faceVertexUvs.length; i < il; i ++ ) {
 
-				var m = geometry.vertices.length;
+			faceVertexUvs[ i ] = [];
 
-				var triA = face.clone();
-				var triB = face.clone();
+		}
 
-				if ( dab >= dbc && dab >= dac ) {
+		for ( var i = 0, il = geometry.faces.length; i < il; i ++ ) {
 
-					var vm = va.clone();
-					vm.lerp( vb, 0.5 );
+			const face = geometry.faces[ i ];
 
-					triA.a = a;
-					triA.b = m;
-					triA.c = c;
+			if ( face instanceof THREE.Face3 ) {
 
-					triB.a = m;
-					triB.b = b;
-					triB.c = c;
+				const a = face.a;
+				const b = face.b;
+				const c = face.c;
 
-					if ( face.vertexNormals.length === 3 ) {
+				const va = geometry.vertices[ a ];
+				const vb = geometry.vertices[ b ];
+				const vc = geometry.vertices[ c ];
 
-						var vnm = face.vertexNormals[ 0 ].clone();
-						vnm.lerp( face.vertexNormals[ 1 ], 0.5 );
+				const dab = va.distanceToSquared( vb );
+				const dbc = vb.distanceToSquared( vc );
+				const dac = va.distanceToSquared( vc );
 
-						triA.vertexNormals[ 1 ].copy( vnm );
-						triB.vertexNormals[ 0 ].copy( vnm );
+				const limitReached = ( faces.length + il - i ) >= this.maxFaces;
 
-					}
+				if ( ! limitReached && ( dab > maxEdgeLengthSquared || dbc > maxEdgeLengthSquared || dac > maxEdgeLengthSquared ) ) {
 
-					if ( face.vertexColors.length === 3 ) {
+					finalized = false;
 
-						var vcm = face.vertexColors[ 0 ].clone();
-						vcm.lerp( face.vertexColors[ 1 ], 0.5 );
+					const m = geometry.vertices.length;
 
-						triA.vertexColors[ 1 ].copy( vcm );
-						triB.vertexColors[ 0 ].copy( vcm );
+					const triA = face.clone();
+					const triB = face.clone();
 
-					}
+					if ( dab >= dbc && dab >= dac ) {
 
-					edge = 0;
+						var vm = va.clone();
+						vm.lerp( vb, 0.5 );
 
-				} else if ( dbc >= dab && dbc >= dac ) {
+						triA.a = a;
+						triA.b = m;
+						triA.c = c;
 
-					var vm = vb.clone();
-					vm.lerp( vc, 0.5 );
+						triB.a = m;
+						triB.b = b;
+						triB.c = c;
 
-					triA.a = a;
-					triA.b = b;
-					triA.c = m;
+						if ( face.vertexNormals.length === 3 ) {
 
-					triB.a = m;
-					triB.b = c;
-					triB.c = a;
+							var vnm = face.vertexNormals[ 0 ].clone();
+							vnm.lerp( face.vertexNormals[ 1 ], 0.5 );
 
-					if ( face.vertexNormals.length === 3 ) {
+							triA.vertexNormals[ 1 ].copy( vnm );
+							triB.vertexNormals[ 0 ].copy( vnm );
 
-						var vnm = face.vertexNormals[ 1 ].clone();
-						vnm.lerp( face.vertexNormals[ 2 ], 0.5 );
+						}
 
-						triA.vertexNormals[ 2 ].copy( vnm );
+						if ( face.vertexColors.length === 3 ) {
 
-						triB.vertexNormals[ 0 ].copy( vnm );
-						triB.vertexNormals[ 1 ].copy( face.vertexNormals[ 2 ] );
-						triB.vertexNormals[ 2 ].copy( face.vertexNormals[ 0 ] );
+							var vcm = face.vertexColors[ 0 ].clone();
+							vcm.lerp( face.vertexColors[ 1 ], 0.5 );
 
-					}
+							triA.vertexColors[ 1 ].copy( vcm );
+							triB.vertexColors[ 0 ].copy( vcm );
 
-					if ( face.vertexColors.length === 3 ) {
+						}
 
-						var vcm = face.vertexColors[ 1 ].clone();
-						vcm.lerp( face.vertexColors[ 2 ], 0.5 );
+						edge = 0;
 
-						triA.vertexColors[ 2 ].copy( vcm );
+					} else if ( dbc >= dab && dbc >= dac ) {
 
-						triB.vertexColors[ 0 ].copy( vcm );
-						triB.vertexColors[ 1 ].copy( face.vertexColors[ 2 ] );
-						triB.vertexColors[ 2 ].copy( face.vertexColors[ 0 ] );
+						var vm = vb.clone();
+						vm.lerp( vc, 0.5 );
 
-					}
+						triA.a = a;
+						triA.b = b;
+						triA.c = m;
 
-					edge = 1;
+						triB.a = m;
+						triB.b = c;
+						triB.c = a;
 
-				} else {
+						if ( face.vertexNormals.length === 3 ) {
 
-					var vm = va.clone();
-					vm.lerp( vc, 0.5 );
+							var vnm = face.vertexNormals[ 1 ].clone();
+							vnm.lerp( face.vertexNormals[ 2 ], 0.5 );
 
-					triA.a = a;
-					triA.b = b;
-					triA.c = m;
+							triA.vertexNormals[ 2 ].copy( vnm );
 
-					triB.a = m;
-					triB.b = b;
-					triB.c = c;
+							triB.vertexNormals[ 0 ].copy( vnm );
+							triB.vertexNormals[ 1 ].copy( face.vertexNormals[ 2 ] );
+							triB.vertexNormals[ 2 ].copy( face.vertexNormals[ 0 ] );
 
-					if ( face.vertexNormals.length === 3 ) {
+						}
 
-						var vnm = face.vertexNormals[ 0 ].clone();
-						vnm.lerp( face.vertexNormals[ 2 ], 0.5 );
+						if ( face.vertexColors.length === 3 ) {
 
-						triA.vertexNormals[ 2 ].copy( vnm );
-						triB.vertexNormals[ 0 ].copy( vnm );
+							var vcm = face.vertexColors[ 1 ].clone();
+							vcm.lerp( face.vertexColors[ 2 ], 0.5 );
 
-					}
+							triA.vertexColors[ 2 ].copy( vcm );
+
+							triB.vertexColors[ 0 ].copy( vcm );
+							triB.vertexColors[ 1 ].copy( face.vertexColors[ 2 ] );
+							triB.vertexColors[ 2 ].copy( face.vertexColors[ 0 ] );
+
+						}
+
+						edge = 1;
+
+					} else {
+
+						var vm = va.clone();
+						vm.lerp( vc, 0.5 );
+
+						triA.a = a;
+						triA.b = b;
+						triA.c = m;
+
+						triB.a = m;
+						triB.b = b;
+						triB.c = c;
 
-					if ( face.vertexColors.length === 3 ) {
+						if ( face.vertexNormals.length === 3 ) {
 
-						var vcm = face.vertexColors[ 0 ].clone();
-						vcm.lerp( face.vertexColors[ 2 ], 0.5 );
+							var vnm = face.vertexNormals[ 0 ].clone();
+							vnm.lerp( face.vertexNormals[ 2 ], 0.5 );
 
-						triA.vertexColors[ 2 ].copy( vcm );
-						triB.vertexColors[ 0 ].copy( vcm );
+							triA.vertexNormals[ 2 ].copy( vnm );
+							triB.vertexNormals[ 0 ].copy( vnm );
+
+						}
+
+						if ( face.vertexColors.length === 3 ) {
+
+							var vcm = face.vertexColors[ 0 ].clone();
+							vcm.lerp( face.vertexColors[ 2 ], 0.5 );
+
+							triA.vertexColors[ 2 ].copy( vcm );
+							triB.vertexColors[ 0 ].copy( vcm );
+
+						}
+
+						edge = 2;
 
 					}
 
-					edge = 2;
+					faces.push( triA, triB );
+					geometry.vertices.push( vm );
 
-				}
+					for ( var j = 0, jl = geometry.faceVertexUvs.length; j < jl; j ++ ) {
 
-				faces.push( triA, triB );
-				geometry.vertices.push( vm );
+						if ( geometry.faceVertexUvs[ j ].length ) {
 
-				for ( var j = 0, jl = geometry.faceVertexUvs.length; j < jl; j ++ ) {
+							const uvs = geometry.faceVertexUvs[ j ][ i ];
 
-					if ( geometry.faceVertexUvs[ j ].length ) {
+							const uvA = uvs[ 0 ];
+							const uvB = uvs[ 1 ];
+							const uvC = uvs[ 2 ];
 
-						var uvs = geometry.faceVertexUvs[ j ][ i ];
+							// AB
 
-						var uvA = uvs[ 0 ];
-						var uvB = uvs[ 1 ];
-						var uvC = uvs[ 2 ];
+							if ( edge === 0 ) {
 
-						// AB
+								var uvM = uvA.clone();
+								uvM.lerp( uvB, 0.5 );
 
-						if ( edge === 0 ) {
+								var uvsTriA = [ uvA.clone(), uvM.clone(), uvC.clone() ];
+								var uvsTriB = [ uvM.clone(), uvB.clone(), uvC.clone() ];
 
-							var uvM = uvA.clone();
-							uvM.lerp( uvB, 0.5 );
+								// BC
 
-							var uvsTriA = [ uvA.clone(), uvM.clone(), uvC.clone() ];
-							var uvsTriB = [ uvM.clone(), uvB.clone(), uvC.clone() ];
+							} else if ( edge === 1 ) {
 
-							// BC
+								var uvM = uvB.clone();
+								uvM.lerp( uvC, 0.5 );
 
-						} else if ( edge === 1 ) {
+								var uvsTriA = [ uvA.clone(), uvB.clone(), uvM.clone() ];
+								var uvsTriB = [ uvM.clone(), uvC.clone(), uvA.clone() ];
 
-							var uvM = uvB.clone();
-							uvM.lerp( uvC, 0.5 );
+								// AC
 
-							var uvsTriA = [ uvA.clone(), uvB.clone(), uvM.clone() ];
-							var uvsTriB = [ uvM.clone(), uvC.clone(), uvA.clone() ];
+							} else {
 
-							// AC
+								var uvM = uvA.clone();
+								uvM.lerp( uvC, 0.5 );
 
-						} else {
+								var uvsTriA = [ uvA.clone(), uvB.clone(), uvM.clone() ];
+								var uvsTriB = [ uvM.clone(), uvB.clone(), uvC.clone() ];
 
-							var uvM = uvA.clone();
-							uvM.lerp( uvC, 0.5 );
+							}
 
-							var uvsTriA = [ uvA.clone(), uvB.clone(), uvM.clone() ];
-							var uvsTriB = [ uvM.clone(), uvB.clone(), uvC.clone() ];
+							faceVertexUvs[ j ].push( uvsTriA, uvsTriB );
 
 						}
 
-						faceVertexUvs[ j ].push( uvsTriA, uvsTriB );
-
 					}
 
-				}
+				} else {
 
-			} else {
+					faces.push( face );
 
-				faces.push( face );
+					for ( var j = 0, jl = geometry.faceVertexUvs.length; j < jl; j ++ ) {
 
-				for ( var j = 0, jl = geometry.faceVertexUvs.length; j < jl; j ++ ) {
+						faceVertexUvs[ j ].push( geometry.faceVertexUvs[ j ][ i ] );
 
-					faceVertexUvs[ j ].push( geometry.faceVertexUvs[ j ][ i ] );
+					}
 
 				}
 
@@ -226,9 +256,19 @@ THREE.TessellateModifier.prototype.modify = function ( geometry ) {
 
 		}
 
+		geometry.faces = faces;
+		geometry.faceVertexUvs = faceVertexUvs;
+
 	}
 
-	geometry.faces = faces;
-	geometry.faceVertexUvs = faceVertexUvs;
+	if ( isBufferGeometry ) {
+
+		return new THREE.BufferGeometry().fromGeometry( geometry );
+
+	} else {
+
+		return geometry;
+
+	}
 
 };

+ 0 - 23
examples/jsm/exporters/GLTFExporter.js

@@ -98,7 +98,6 @@ GLTFExporter.prototype = {
 			embedImages: true,
 			maxTextureSize: Infinity,
 			animations: [],
-			forcePowerOfTwoTextures: false,
 			includeCustomExtensions: false
 		};
 
@@ -246,19 +245,6 @@ GLTFExporter.prototype = {
 
 		}
 
-		/**
-		 * Checks if image size is POT.
-		 *
-		 * @param {Image} image The image to be checked.
-		 * @returns {Boolean} Returns true if image size is POT.
-		 *
-		 */
-		function isPowerOfTwo( image ) {
-
-			return MathUtils.isPowerOfTwo( image.width ) && MathUtils.isPowerOfTwo( image.height );
-
-		}
-
 		/**
 		 * Checks if normal attribute values are normalized.
 		 *
@@ -803,15 +789,6 @@ GLTFExporter.prototype = {
 				canvas.width = Math.min( image.width, options.maxTextureSize );
 				canvas.height = Math.min( image.height, options.maxTextureSize );
 
-				if ( options.forcePowerOfTwoTextures && ! isPowerOfTwo( canvas ) ) {
-
-					console.warn( 'GLTFExporter: Resized non-power-of-two image.', image );
-
-					canvas.width = MathUtils.floorPowerOfTwo( canvas.width );
-					canvas.height = MathUtils.floorPowerOfTwo( canvas.height );
-
-				}
-
 				var ctx = canvas.getContext( '2d' );
 
 				if ( flipY === true ) {

+ 86 - 14
examples/jsm/geometries/RoundedBoxBufferGeometry.js

@@ -3,12 +3,55 @@ import {
 	Vector3
 } from "../../../build/three.module.js";
 
+const tempNormal = new Vector3();
+function getUv( faceDirVector, normal, uvAxis, projectionAxis, radius, sideLength ) {
+
+	const totArcLength = 2 * Math.PI * radius / 4;
+
+	// length of the planes between the arcs on each axis
+	const centerLength = Math.max( sideLength - 2 * radius, 0 );
+	const halfArc = Math.PI / 4;
+
+	// Get the vector projected onto the Y plane
+	tempNormal.copy( normal );
+	tempNormal[ projectionAxis ] = 0;
+	tempNormal.normalize();
+
+	// total amount of UV space alloted to a single arc
+	const arcUvRatio = 0.5 * totArcLength / ( totArcLength + centerLength );
+
+	// the distance along one arc the point is at
+	const arcAngleRatio = 1.0 - ( tempNormal.angleTo( faceDirVector ) / halfArc );
+
+	if ( Math.sign( tempNormal[ uvAxis ] ) === 1 ) {
+
+		return arcAngleRatio * arcUvRatio;
+
+	} else {
+
+		// total amount of UV space alloted to the plane between the arcs
+		const lenUv = centerLength / ( totArcLength + centerLength );
+		return lenUv + arcUvRatio + arcUvRatio * ( 1.0 - arcAngleRatio );
+
+	}
+
+}
+
 class RoundedBoxBufferGeometry extends BoxBufferGeometry {
 
-	constructor( width = 1, height = 1, depth = 1, segments = 1, radius = 1 ) {
+	constructor( width = 1, height = 1, depth = 1, segments = 2, radius = 0.1 ) {
+
+		// ensure segments is odd so we have a plane connecting the rounded corners
+		segments = segments * 2 + 1;
+
+		// ensure radius isn't bigger than shortest side
+		radius = Math.min( width / 2, height / 2, depth / 2, radius );
 
 		super( 1, 1, 1, segments, segments, segments );
 
+		// if we just have one segment we're the same as a regular box
+		if ( segments === 1 ) return;
+
 		const geometry2 = this.toNonIndexed();
 
 		this.index = null;
@@ -28,11 +71,17 @@ class RoundedBoxBufferGeometry extends BoxBufferGeometry {
 		const uvs = this.attributes.uv.array;
 
 		const faceTris = positions.length / 6;
+		const faceDirVector = new Vector3();
+		const halfSegmentSize = 0.5 / segments;
 
 		for ( let i = 0, j = 0; i < positions.length; i += 3, j += 2 ) {
 
 			position.fromArray( positions, i );
-			normal.copy( position ).normalize();
+			normal.copy( position );
+			normal.x -= Math.sign( normal.x ) * halfSegmentSize;
+			normal.y -= Math.sign( normal.y ) * halfSegmentSize;
+			normal.z -= Math.sign( normal.z ) * halfSegmentSize;
+			normal.normalize();
 
 			positions[ i + 0 ] = box.x * Math.sign( position.x ) + normal.x * radius;
 			positions[ i + 1 ] = box.y * Math.sign( position.y ) + normal.y * radius;
@@ -47,28 +96,51 @@ class RoundedBoxBufferGeometry extends BoxBufferGeometry {
 			switch ( side ) {
 
 				case 0: // right
-					uvs[ j + 0 ] = 0.5 - ( positions[ i + 2 ] / ( depth - radius ) );
-					uvs[ j + 1 ] = 0.5 + ( positions[ i + 1 ] / ( height - radius ) );
+
+					// generate UVs along Z then Y
+					faceDirVector.set( 1, 0, 0 );
+					uvs[ j + 0 ] = getUv( faceDirVector, normal, 'z', 'y', radius, depth );
+					uvs[ j + 1 ] = 1.0 - getUv( faceDirVector, normal, 'y', 'z', radius, height );
 					break;
+
 				case 1: // left
-					uvs[ j + 0 ] = 0.5 + ( positions[ i + 2 ] / ( depth - radius ) );
-					uvs[ j + 1 ] = 0.5 + ( positions[ i + 1 ] / ( height - radius ) );
+
+					// generate UVs along Z then Y
+					faceDirVector.set( - 1, 0, 0 );
+					uvs[ j + 0 ] = 1.0 - getUv( faceDirVector, normal, 'z', 'y', radius, depth );
+					uvs[ j + 1 ] = 1.0 - getUv( faceDirVector, normal, 'y', 'z', radius, height );
 					break;
+
 				case 2: // top
-					uvs[ j + 0 ] = 0.5 + ( positions[ i + 0 ] / ( width - radius ) );
-					uvs[ j + 1 ] = 0.5 - ( positions[ i + 2 ] / ( depth - radius ) );
+
+					// generate UVs along X then Z
+					faceDirVector.set( 0, 1, 0 );
+					uvs[ j + 0 ] = 1.0 - getUv( faceDirVector, normal, 'x', 'z', radius, width );
+					uvs[ j + 1 ] = getUv( faceDirVector, normal, 'z', 'x', radius, depth );
 					break;
+
 				case 3: // bottom
-					uvs[ j + 0 ] = 0.5 + ( positions[ i + 0 ] / ( width - radius ) );
-					uvs[ j + 1 ] = 0.5 + ( positions[ i + 2 ] / ( depth - radius ) );
+
+					// generate UVs along X then Z
+					faceDirVector.set( 0, - 1, 0 );
+					uvs[ j + 0 ] = 1.0 - getUv( faceDirVector, normal, 'x', 'z', radius, width );
+					uvs[ j + 1 ] = 1.0 - getUv( faceDirVector, normal, 'z', 'x', radius, depth );
 					break;
+
 				case 4: // front
-					uvs[ j + 0 ] = 0.5 + ( positions[ i + 0 ] / ( width - radius ) );
-					uvs[ j + 1 ] = 0.5 + ( positions[ i + 1 ] / ( height - radius ) );
+
+					// generate UVs along X then Y
+					faceDirVector.set( 0, 0, 1 );
+					uvs[ j + 0 ] = 1.0 - getUv( faceDirVector, normal, 'x', 'y', radius, width );
+					uvs[ j + 1 ] = 1.0 - getUv( faceDirVector, normal, 'y', 'x', radius, height );
 					break;
+
 				case 5: // back
-					uvs[ j + 0 ] = 0.5 - ( positions[ i + 0 ] / ( width - radius ) );
-					uvs[ j + 1 ] = 0.5 + ( positions[ i + 1 ] / ( height - radius ) );
+
+					// generate UVs along X then Y
+					faceDirVector.set( 0, 0, - 1 );
+					uvs[ j + 0 ] = getUv( faceDirVector, normal, 'x', 'y', radius, width );
+					uvs[ j + 1 ] = 1.0 - getUv( faceDirVector, normal, 'y', 'x', radius, height );
 					break;
 
 			}

+ 2 - 2
examples/jsm/loaders/FBXLoader.js

@@ -542,7 +542,7 @@ var FBXLoader = ( function () {
 
 				parameters.color = new Color().fromArray( materialNode.Diffuse.value );
 
-			} else if ( materialNode.DiffuseColor && materialNode.DiffuseColor.type === 'Color' ) {
+			} else if ( materialNode.DiffuseColor && ( materialNode.DiffuseColor.type === 'Color' || materialNode.DiffuseColor.type === 'ColorRGB' ) ) {
 
 				// The blender exporter exports diffuse here instead of in materialNode.Diffuse
 				parameters.color = new Color().fromArray( materialNode.DiffuseColor.value );
@@ -559,7 +559,7 @@ var FBXLoader = ( function () {
 
 				parameters.emissive = new Color().fromArray( materialNode.Emissive.value );
 
-			} else if ( materialNode.EmissiveColor && materialNode.EmissiveColor.type === 'Color' ) {
+			} else if ( materialNode.EmissiveColor && ( materialNode.EmissiveColor.type === 'Color' || materialNode.EmissiveColor.type === 'ColorRGB' ) ) {
 
 				// The blender exporter exports emissive color here instead of in materialNode.Emissive
 				parameters.emissive = new Color().fromArray( materialNode.EmissiveColor.value );

+ 3 - 0
examples/jsm/loaders/GLTFLoader.js

@@ -3256,6 +3256,7 @@ var GLTFLoader = ( function () {
 
 		var parser = this;
 		var json = this.json;
+		var extensions = this.extensions;
 
 		var meshDef = json.meshes[ meshIndex ];
 		var primitives = meshDef.primitives;
@@ -3352,6 +3353,8 @@ var GLTFLoader = ( function () {
 
 				assignExtrasToUserData( mesh, meshDef );
 
+				if ( primitive.extensions ) addUnknownExtensionsToUserData( extensions, mesh, primitive );
+
 				parser.assignFinalMaterial( mesh );
 
 				meshes.push( mesh );

+ 48 - 37
examples/jsm/loaders/KTX2Loader.js

@@ -4,11 +4,8 @@
  * - DFD: https://www.khronos.org/registry/DataFormat/specs/1.3/dataformat.1.3.html#basicdescriptor
  *
  * To do:
- * - [ ] Cross-platform testing
- * - [ ] Specify JS/WASM transcoder path
  * - [ ] High-quality demo
  * - [ ] Documentation
- * - [ ] TypeScript definitions
  * - [ ] (Optional) Include BC5
  * - [ ] (Optional) Include EAC RG on mobile (WEBGL_compressed_texture_etc)
  * - [ ] (Optional) Include two-texture output mode (see: clearcoat + clearcoatRoughness)
@@ -136,20 +133,24 @@ class KTX2Loader extends CompressedTextureLoader {
 
 		} );
 
+		// parse() will call initModule() again, but starting the process early
+		// should allow the WASM to load in parallel with the texture.
 		this.initModule();
 
-		Promise.all( [ bufferPending, this.basisModulePending ] ).then( function ( [ buffer ] ) {
+		Promise.all( [ bufferPending, this.basisModulePending ] )
+			.then( function ( [ buffer ] ) {
 
-			scope.parse( buffer, function ( _texture ) {
+				scope.parse( buffer, function ( _texture ) {
 
-				texture.copy( _texture );
-				texture.needsUpdate = true;
+					texture.copy( _texture );
+					texture.needsUpdate = true;
 
-				if ( onLoad ) onLoad( texture );
+					if ( onLoad ) onLoad( texture );
 
-			}, onError );
+				}, onError );
 
-		} );
+			} )
+			.catch( onError );
 
 		return texture;
 
@@ -157,40 +158,50 @@ class KTX2Loader extends CompressedTextureLoader {
 
 	parse( buffer, onLoad, onError ) {
 
-		var BasisLzEtc1sImageTranscoder = this.basisModule.BasisLzEtc1sImageTranscoder;
-		var UastcImageTranscoder = this.basisModule.UastcImageTranscoder;
-		var TextureFormat = this.basisModule.TextureFormat;
+		var scope = this;
 
-		var ktx = new KTX2Container( this.basisModule, buffer );
+		// load() may have already called initModule(), but call it again here
+		// in case the user called parse() directly. Method is idempotent.
+		this.initModule();
 
-		// TODO(donmccurdy): Should test if texture is transcodable before attempting
-		// any transcoding. If supercompressionScheme is KTX_SS_BASIS_LZ and dfd
-		// colorModel is ETC1S (163) or if dfd colorModel is UASTCF (166)
-		// then texture must be transcoded.
-		var transcoder = ktx.getTexFormat() === TextureFormat.UASTC4x4
-			? new UastcImageTranscoder()
-			: new BasisLzEtc1sImageTranscoder();
+		this.basisModulePending.then( function () {
 
-		ktx.initMipmaps( transcoder, this.transcoderConfig )
-			.then( function () {
+			var BasisLzEtc1sImageTranscoder = scope.basisModule.BasisLzEtc1sImageTranscoder;
+			var UastcImageTranscoder = scope.basisModule.UastcImageTranscoder;
+			var TextureFormat = scope.basisModule.TextureFormat;
 
-				var texture = new CompressedTexture(
-					ktx.mipmaps,
-					ktx.getWidth(),
-					ktx.getHeight(),
-					ktx.transcodedFormat,
-					UnsignedByteType
-				);
+			var ktx = new KTX2Container( scope.basisModule, buffer );
 
-				texture.encoding = ktx.getEncoding();
-				texture.premultiplyAlpha = ktx.getPremultiplyAlpha();
-				texture.minFilter = ktx.mipmaps.length === 1 ? LinearFilter : LinearMipmapLinearFilter;
-				texture.magFilter = LinearFilter;
+			// TODO(donmccurdy): Should test if texture is transcodable before attempting
+			// any transcoding. If supercompressionScheme is KTX_SS_BASIS_LZ and dfd
+			// colorModel is ETC1S (163) or if dfd colorModel is UASTCF (166)
+			// then texture must be transcoded.
+			var transcoder = ktx.getTexFormat() === TextureFormat.UASTC4x4
+				? new UastcImageTranscoder()
+				: new BasisLzEtc1sImageTranscoder();
 
-				onLoad( texture );
+			ktx.initMipmaps( transcoder, scope.transcoderConfig )
+				.then( function () {
 
-			} )
-			.catch( onError );
+					var texture = new CompressedTexture(
+						ktx.mipmaps,
+						ktx.getWidth(),
+						ktx.getHeight(),
+						ktx.transcodedFormat,
+						UnsignedByteType
+					);
+
+					texture.encoding = ktx.getEncoding();
+					texture.premultiplyAlpha = ktx.getPremultiplyAlpha();
+					texture.minFilter = ktx.mipmaps.length === 1 ? LinearFilter : LinearMipmapLinearFilter;
+					texture.magFilter = LinearFilter;
+
+					onLoad( texture );
+
+				} )
+				.catch( onError );
+
+		} );
 
 		return this;
 

+ 1 - 0
examples/jsm/loaders/LottieLoader.js

@@ -25,6 +25,7 @@ LottieLoader.prototype = Object.assign( Object.create( Loader.prototype ), {
 		const quality = this._quality || 1;
 
 		const texture = new CanvasTexture();
+		texture.anisotropy = 16;
 
 		const loader = new FileLoader( this.manager );
 		loader.setPath( this.path );

+ 14 - 0
examples/jsm/loaders/SVGLoader.js

@@ -399,6 +399,9 @@ SVGLoader.prototype = Object.assign( Object.create( Loader.prototype ), {
 
 						for ( var j = 0, jl = numbers.length; j < jl; j += 7 ) {
 
+							// skip command if start point == end point
+							if ( numbers[ j + 5 ] == point.x && numbers[ j + 6 ] == point.y ) continue;
+
 							var start = point.clone();
 							point.x = numbers[ j + 5 ];
 							point.y = numbers[ j + 6 ];
@@ -588,6 +591,9 @@ SVGLoader.prototype = Object.assign( Object.create( Loader.prototype ), {
 
 						for ( var j = 0, jl = numbers.length; j < jl; j += 7 ) {
 
+							// skip command if no displacement
+							if ( numbers[ j + 5 ] == 0 && numbers[ j + 6 ] == 0 ) continue;
+
 							var start = point.clone();
 							point.x += numbers[ j + 5 ];
 							point.y += numbers[ j + 6 ];
@@ -672,6 +678,14 @@ SVGLoader.prototype = Object.assign( Object.create( Loader.prototype ), {
 
 		function parseArcCommand( path, rx, ry, x_axis_rotation, large_arc_flag, sweep_flag, start, end ) {
 
+			if ( rx == 0 || ry == 0 ) {
+
+				// draw a line if either of the radii == 0
+				path.lineTo( end.x, end.y );
+				return;
+
+			}
+
 			x_axis_rotation = x_axis_rotation * Math.PI / 180;
 
 			// Ensure radii are positive

+ 14 - 0
examples/jsm/loaders/XYZLoader.d.ts

@@ -0,0 +1,14 @@
+import {
+	BufferGeometry,
+	Loader,
+	LoadingManager
+} from '../../../src/Three';
+
+export class XYZLoader extends Loader {
+
+	constructor( manager?: LoadingManager );
+
+	load( url: string, onLoad: ( geometry: BufferGeometry ) => void, onProgress?: ( event: ProgressEvent ) => void, onError?: ( event: ErrorEvent ) => void ): void;
+	parse( data: string, onLoad: ( geometry: BufferGeometry ) => void ): object;
+
+}

+ 100 - 0
examples/jsm/loaders/XYZLoader.js

@@ -0,0 +1,100 @@
+import {
+	BufferGeometry,
+	FileLoader,
+	Float32BufferAttribute,
+	Loader
+} from '../../../build/three.module.js';
+
+class XYZLoader extends Loader {
+
+	load( url, onLoad, onProgress, onError ) {
+
+		const scope = this;
+
+		const loader = new FileLoader( this.manager );
+		loader.setPath( this.path );
+		loader.setRequestHeader( this.requestHeader );
+		loader.setWithCredentials( this.withCredentials );
+		loader.load( url, function ( text ) {
+
+			try {
+
+				onLoad( scope.parse( text ) );
+
+			} catch ( e ) {
+
+				if ( onError ) {
+
+					onError( e );
+
+				} else {
+
+					console.error( e );
+
+				}
+
+				scope.manager.itemError( url );
+
+			}
+
+		}, onProgress, onError );
+
+	}
+
+	parse( text ) {
+
+		const lines = text.split( '\n' );
+
+		const vertices = [];
+		const colors = [];
+
+		for ( let line of lines ) {
+
+			line = line.trim();
+
+			if ( line.charAt( 0 ) === '#' ) continue; // skip comments
+
+			const lineValues = line.split( /\s+/ );
+
+			if ( lineValues.length === 3 ) {
+
+				// XYZ
+
+				vertices.push( parseFloat( lineValues[ 0 ] ) );
+				vertices.push( parseFloat( lineValues[ 1 ] ) );
+				vertices.push( parseFloat( lineValues[ 2 ] ) );
+
+			}
+
+			if ( lineValues.length === 6 ) {
+
+				// XYZRGB
+
+				vertices.push( parseFloat( lineValues[ 0 ] ) );
+				vertices.push( parseFloat( lineValues[ 1 ] ) );
+				vertices.push( parseFloat( lineValues[ 2 ] ) );
+
+				colors.push( parseFloat( lineValues[ 3 ] ) / 255 );
+				colors.push( parseFloat( lineValues[ 4 ] ) / 255 );
+				colors.push( parseFloat( lineValues[ 5 ] ) / 255 );
+
+			}
+
+		}
+
+		const geometry = new BufferGeometry();
+		geometry.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );
+
+		if ( colors.length > 0 ) {
+
+			geometry.setAttribute( 'color', new Float32BufferAttribute( colors, 3 ) );
+
+		}
+
+		return geometry;
+
+	}
+
+}
+
+export { XYZLoader };

+ 14 - 14
examples/jsm/modifiers/CurveModifier.d.ts

@@ -1,4 +1,4 @@
-import { Geometry, Material, Mesh, Spline } from '../../../build/three.module';
+import { Geometry, Material, Mesh } from '../../../build/three.module';
 import {
 	DataTexture,
 	Curve,
@@ -7,10 +7,6 @@ import {
 	InstancedMesh
 } from '../../../src/Three';
 import { Flow } from './CurveModifier';
-import { modifyShader } from './CurveModifier';
-import { getUniforms } from './CurveModifier';
-import { updateSplineTexture } from './CurveModifier';
-import { initSplineTexture } from "./CurveModifier";
 
 interface SplineUniform {
 	spineTexture: Uniform,
@@ -20,31 +16,35 @@ interface SplineUniform {
 	Uniform,
 	flow: Uniform,
 }
-export function initSplineTexture(size?: number): DataTexture;
+export function initSplineTexture( size?: number ): DataTexture;
 
-export function updateSplineTexture(texture: DataTexture, splineCurve: Curve, offset?: number);
+export function updateSplineTexture( texture: DataTexture, splineCurve: Curve, offset?: number );
 
-export function getUniforms(splineTexture: DataTexture): SplineUniform;
+export function getUniforms( splineTexture: DataTexture ): SplineUniform;
 
-export function modifyShader(material: Material, uniforms: SplineUniform, numberOfCurves?: number);
+export function modifyShader( material: Material, uniforms: SplineUniform, numberOfCurves?: number );
 
 export class Flow {
-	constructor(mesh: Mesh, numberOfCurves?: number);
+
+	constructor( mesh: Mesh, numberOfCurves?: number );
 	curveArray: number[];
 	curveLengthArray: number[];
 	object3D: Mesh;
 	splineTexure: DataTexture;
 	uniforms: SplineUniform;
-	updateCurve(index: number, curve: Curve);
-	moveAlongCurve(amount: number);
+	updateCurve( index: number, curve: Curve );
+	moveAlongCurve( amount: number );
+
 }
 
 export class InstancedFlow extends Flow {
-	constructor(count:Number, curveCount:Number, geometry: Geometry, material: Material);
+
+	constructor( count: Number, curveCount: Number, geometry: Geometry, material: Material );
 	object3D: InstancedMesh;
 	offsets: number[];
 	whichCurve: number[];
 
-	moveIndividualAlongCurve(index: number, offset: number);
+	moveIndividualAlongCurve( index: number, offset: number );
 	setCurve( index: number, curveNo: number )
+
 }

+ 1 - 0
examples/jsm/modifiers/CurveModifier.js

@@ -119,6 +119,7 @@ export function modifyShader( material, uniforms, numberOfCurves = 1 ) {
 		Object.assign( shader.uniforms, uniforms );
 
 		const vertexShader = `
+		#define USE_ENVMAP
 		uniform sampler2D spineTexture;
 		uniform float pathOffset;
 		uniform float pathSegment;

+ 84 - 44
examples/jsm/modifiers/EdgeSplitModifier.js

@@ -1,25 +1,30 @@
-import { BufferAttribute, BufferGeometry, Vector3 } from "../../../build/three.module.js";
+import {
+	BufferAttribute,
+	BufferGeometry,
+	Vector3
+} from "../../../build/three.module.js";
 import { BufferGeometryUtils } from "../utils/BufferGeometryUtils.js";
 
 
-export function EdgeSplitModifier() {
+var EdgeSplitModifier = function () {
 
-	const A = new Vector3();
-	const B = new Vector3();
-	const C = new Vector3();
+	var A = new Vector3();
+	var B = new Vector3();
+	var C = new Vector3();
 
-	let positions, normals;
-	let indexes;
-	let pointToIndexMap, splitIndexes;
+	var positions, normals;
+	var indexes;
+	var pointToIndexMap, splitIndexes;
 
 
 	function computeNormals() {
 
 		normals = new Float32Array( indexes.length * 3 );
 
-		for ( let i = 0; i < indexes.length; i += 3 ) {
+		for ( var i = 0; i < indexes.length; i += 3 ) {
+
+			var index = indexes[ i ];
 
-			let index = indexes[ i ];
 			A.set(
 				positions[ 3 * index ],
 				positions[ 3 * index + 1 ],
@@ -40,9 +45,9 @@ export function EdgeSplitModifier() {
 			C.sub( B );
 			A.sub( B );
 
-			const normal = C.cross( A ).normalize();
+			var normal = C.cross( A ).normalize();
 
-			for ( let j = 0; j < 3; j ++ ) {
+			for ( var j = 0; j < 3; j ++ ) {
 
 				normals[ 3 * ( i + j ) ] = normal.x;
 				normals[ 3 * ( i + j ) + 1 ] = normal.y;
@@ -59,13 +64,16 @@ export function EdgeSplitModifier() {
 
 		pointToIndexMap = Array( positions.length / 3 );
 
-		for ( let i = 0; i < indexes.length; i ++ ) {
+		for ( var i = 0; i < indexes.length; i ++ ) {
+
+			var index = indexes[ i ];
 
-			const index = indexes[ i ];
+			if ( pointToIndexMap[ index ] == null ) {
 
-			if ( pointToIndexMap[ index ] == null )
 				pointToIndexMap[ index ] = [];
 
+			}
+
 			pointToIndexMap[ index ].push( i );
 
 		}
@@ -75,27 +83,29 @@ export function EdgeSplitModifier() {
 
 	function edgeSplitToGroups( indexes, cutOff, firstIndex ) {
 
-		A.set( normals[ 3 * firstIndex ], normals[ 3 * firstIndex + 1 ], normals[ 3 * firstIndex + 2 ] )
-			.normalize();
+		A.set( normals[ 3 * firstIndex ], normals[ 3 * firstIndex + 1 ], normals[ 3 * firstIndex + 2 ] ).normalize();
 
-		const result = {
+		var result = {
 			splitGroup: [],
 			currentGroup: [ firstIndex ]
 		};
 
-		for ( const j of indexes ) {
+		for ( var j of indexes ) {
 
 			if ( j !== firstIndex ) {
 
-				B.set( normals[ 3 * j ], normals[ 3 * j + 1 ], normals[ 3 * j + 2 ] )
-					.normalize();
+				B.set( normals[ 3 * j ], normals[ 3 * j + 1 ], normals[ 3 * j + 2 ] ).normalize();
+
+				if ( B.dot( A ) < cutOff ) {
 
-				if ( B.dot( A ) < cutOff )
 					result.splitGroup.push( j );
 
-				else
+				} else {
+
 					result.currentGroup.push( j );
 
+				}
+
 			}
 
 		}
@@ -107,27 +117,44 @@ export function EdgeSplitModifier() {
 
 	function edgeSplit( indexes, cutOff, original = null ) {
 
-		if ( indexes.length === 0 )
-			return;
+		if ( indexes.length === 0 ) return;
+
+		var groupResults = [];
+
+		for ( var index of indexes ) {
 
-		const groupResults = [];
-		for ( const index of indexes )
 			groupResults.push( edgeSplitToGroups( indexes, cutOff, index ) );
 
-		let result = groupResults[ 0 ];
-		for ( const groupResult of groupResults )
-			if ( groupResult.currentGroup.length > result.currentGroup.length )
+		}
+
+		var result = groupResults[ 0 ];
+
+		for ( var groupResult of groupResults ) {
+
+			if ( groupResult.currentGroup.length > result.currentGroup.length ) {
+
 				result = groupResult;
 
-		if ( original != null )
+			}
+
+		}
+
+
+		if ( original != null ) {
+
 			splitIndexes.push( {
 				original: original,
 				indexes: result.currentGroup
 			} );
 
-		if ( result.splitGroup.length )
+		}
+
+		if ( result.splitGroup.length ) {
+
 			edgeSplit( result.splitGroup, cutOff, original || result.currentGroup[ 0 ] );
 
+		}
+
 	}
 
 
@@ -140,44 +167,55 @@ export function EdgeSplitModifier() {
 		}
 
 
-		if ( geometry.index == null )
+		if ( geometry.index == null ) {
+
+			if ( BufferGeometryUtils === undefined ) {
+
+			 	throw 'THREE.EdgeSplitModifier relies on BufferGeometryUtils';
+
+			}
+
 			geometry = BufferGeometryUtils.mergeVertices( geometry );
 
+		}
 
 		indexes = geometry.index.array;
 		positions = geometry.getAttribute( "position" ).array;
 
 		computeNormals();
-
-
 		mapPositionsToIndexes();
 
 
 		splitIndexes = [];
 
-		for ( const vertexIndexes of pointToIndexMap )
+		for ( var vertexIndexes of pointToIndexMap ) {
+
 			edgeSplit( vertexIndexes, Math.cos( cutOffAngle ) - 0.001 );
 
+		}
 
-		const newPositions = new Float32Array( positions.length + 3 * splitIndexes.length );
+		var newPositions = new Float32Array( positions.length + 3 * splitIndexes.length );
 		newPositions.set( positions );
-		const offset = positions.length;
+		var offset = positions.length;
 
-		const newIndexes = new Uint32Array( indexes.length );
+		var newIndexes = new Uint32Array( indexes.length );
 		newIndexes.set( indexes );
 
-		for ( let i = 0; i < splitIndexes.length; i ++ ) {
+		for ( var i = 0; i < splitIndexes.length; i ++ ) {
 
-			const split = splitIndexes[ i ];
-			const index = indexes[ split.original ];
+			var split = splitIndexes[ i ];
+			var index = indexes[ split.original ];
 
 			newPositions[ offset + 3 * i ] = positions[ 3 * index ];
 			newPositions[ offset + 3 * i + 1 ] = positions[ 3 * index + 1 ];
 			newPositions[ offset + 3 * i + 2 ] = positions[ 3 * index + 2 ];
 
-			for ( const j of split.indexes )
+			for ( var j of split.indexes ) {
+
 				newIndexes[ j ] = offset / 3 + i;
 
+			}
+
 		}
 
 		geometry = new BufferGeometry();
@@ -188,4 +226,6 @@ export function EdgeSplitModifier() {
 
 	};
 
-}
+};
+
+export { EdgeSplitModifier };

+ 1 - 1
examples/jsm/modifiers/SubdivisionModifier.d.ts

@@ -8,7 +8,7 @@ export class SubdivisionModifier {
 	constructor( subdivisions?: number );
 	subdivisions: number;
 
-	modify( geometry: BufferGeometry | Geometry ): Geometry;
+	modify( geometry: Geometry | BufferGeometry ): Geometry | BufferGeometry;
 	smooth( geometry: Geometry ): void;
 
 }

+ 14 - 3
examples/jsm/modifiers/SubdivisionModifier.js

@@ -1,4 +1,5 @@
 import {
+	BufferGeometry,
 	Face3,
 	Geometry,
 	Vector2,
@@ -27,7 +28,9 @@ var SubdivisionModifier = function ( subdivisions ) {
 // Applies the "modify" pattern
 SubdivisionModifier.prototype.modify = function ( geometry ) {
 
-	if ( geometry.isBufferGeometry ) {
+	var isBufferGeometry = geometry.isBufferGeometry;
+
+	if ( isBufferGeometry ) {
 
 		geometry = new Geometry().fromBufferGeometry( geometry );
 
@@ -37,7 +40,7 @@ SubdivisionModifier.prototype.modify = function ( geometry ) {
 
 	}
 
-	geometry.mergeVertices();
+	geometry.mergeVertices( 6 );
 
 	var repeats = this.subdivisions;
 
@@ -50,7 +53,15 @@ SubdivisionModifier.prototype.modify = function ( geometry ) {
 	geometry.computeFaceNormals();
 	geometry.computeVertexNormals();
 
-	return geometry;
+	if ( isBufferGeometry ) {
+
+		return new BufferGeometry().fromGeometry( geometry );
+
+	} else {
+
+		return geometry;
+
+	}
 
 };
 

+ 7 - 4
examples/jsm/modifiers/TessellateModifier.d.ts

@@ -1,12 +1,15 @@
 import {
-	Geometry
+	Geometry,
+	BufferGeometry
 } from '../../../src/Three';
 
 export class TessellateModifier {
 
-	constructor( maxEdgeLength: number );
-	maxEdgeLength: number;
+	constructor( maxEdgeLength?: number, maxIterations?: number, maxFaces?: number );
+	maxEdgeLength: number = 0.1;
+	maxIterations: number = 6;
+	maxFaces: number = Infinity;
 
-	modify( geometry: Geometry ): void;
+	modify( geometry: Geometry | BufferGeometry ): Geometry | BufferGeometry;
 
 }

+ 172 - 130
examples/jsm/modifiers/TessellateModifier.js

@@ -1,228 +1,260 @@
 import {
-	Face3
+	BufferGeometry,
+	Face3,
+	Geometry
 } from "../../../build/three.module.js";
 
 /**
  * Break faces with edges longer than maxEdgeLength
- * - not recursive
  */
 
-var TessellateModifier = function ( maxEdgeLength ) {
+var TessellateModifier = function ( maxEdgeLength = 0.1, maxIterations = 6, maxFaces = Infinity ) {
 
 	this.maxEdgeLength = maxEdgeLength;
+	this.maxIterations = maxIterations;
+	this.maxFaces = maxFaces;
 
 };
 
+// Applies the "modify" pattern
 TessellateModifier.prototype.modify = function ( geometry ) {
 
-	var edge;
+	const isBufferGeometry = geometry.isBufferGeometry;
 
-	var faces = [];
-	var faceVertexUvs = [];
-	var maxEdgeLengthSquared = this.maxEdgeLength * this.maxEdgeLength;
+	if ( isBufferGeometry ) {
 
-	for ( var i = 0, il = geometry.faceVertexUvs.length; i < il; i ++ ) {
+		geometry = new Geometry().fromBufferGeometry( geometry );
 
-		faceVertexUvs[ i ] = [];
+	} else {
+
+		geometry = geometry.clone();
 
 	}
 
-	for ( var i = 0, il = geometry.faces.length; i < il; i ++ ) {
+	geometry.mergeVertices( 6 );
 
-		var face = geometry.faces[ i ];
+	let finalized = false;
+	let iteration = 0;
+	const maxEdgeLengthSquared = this.maxEdgeLength * this.maxEdgeLength;
 
-		if ( face instanceof Face3 ) {
+	let edge;
 
-			var a = face.a;
-			var b = face.b;
-			var c = face.c;
+	while ( ! finalized && iteration < this.maxIterations && geometry.faces.length < this.maxFaces ) {
 
-			var va = geometry.vertices[ a ];
-			var vb = geometry.vertices[ b ];
-			var vc = geometry.vertices[ c ];
+		const faces = [];
+		const faceVertexUvs = [];
 
-			var dab = va.distanceToSquared( vb );
-			var dbc = vb.distanceToSquared( vc );
-			var dac = va.distanceToSquared( vc );
+		finalized = true;
+		iteration ++;
 
-			if ( dab > maxEdgeLengthSquared || dbc > maxEdgeLengthSquared || dac > maxEdgeLengthSquared ) {
+		for ( var i = 0, il = geometry.faceVertexUvs.length; i < il; i ++ ) {
 
-				var m = geometry.vertices.length;
+			faceVertexUvs[ i ] = [];
 
-				var triA = face.clone();
-				var triB = face.clone();
+		}
 
-				if ( dab >= dbc && dab >= dac ) {
+		for ( var i = 0, il = geometry.faces.length; i < il; i ++ ) {
 
-					var vm = va.clone();
-					vm.lerp( vb, 0.5 );
+			const face = geometry.faces[ i ];
 
-					triA.a = a;
-					triA.b = m;
-					triA.c = c;
+			if ( face instanceof Face3 ) {
 
-					triB.a = m;
-					triB.b = b;
-					triB.c = c;
+				const a = face.a;
+				const b = face.b;
+				const c = face.c;
 
-					if ( face.vertexNormals.length === 3 ) {
+				const va = geometry.vertices[ a ];
+				const vb = geometry.vertices[ b ];
+				const vc = geometry.vertices[ c ];
 
-						var vnm = face.vertexNormals[ 0 ].clone();
-						vnm.lerp( face.vertexNormals[ 1 ], 0.5 );
+				const dab = va.distanceToSquared( vb );
+				const dbc = vb.distanceToSquared( vc );
+				const dac = va.distanceToSquared( vc );
 
-						triA.vertexNormals[ 1 ].copy( vnm );
-						triB.vertexNormals[ 0 ].copy( vnm );
+				const limitReached = ( faces.length + il - i ) >= this.maxFaces;
 
-					}
+				if ( ! limitReached && ( dab > maxEdgeLengthSquared || dbc > maxEdgeLengthSquared || dac > maxEdgeLengthSquared ) ) {
 
-					if ( face.vertexColors.length === 3 ) {
+					finalized = false;
 
-						var vcm = face.vertexColors[ 0 ].clone();
-						vcm.lerp( face.vertexColors[ 1 ], 0.5 );
+					const m = geometry.vertices.length;
 
-						triA.vertexColors[ 1 ].copy( vcm );
-						triB.vertexColors[ 0 ].copy( vcm );
+					const triA = face.clone();
+					const triB = face.clone();
 
-					}
+					if ( dab >= dbc && dab >= dac ) {
 
-					edge = 0;
+						var vm = va.clone();
+						vm.lerp( vb, 0.5 );
 
-				} else if ( dbc >= dab && dbc >= dac ) {
+						triA.a = a;
+						triA.b = m;
+						triA.c = c;
 
-					var vm = vb.clone();
-					vm.lerp( vc, 0.5 );
+						triB.a = m;
+						triB.b = b;
+						triB.c = c;
 
-					triA.a = a;
-					triA.b = b;
-					triA.c = m;
+						if ( face.vertexNormals.length === 3 ) {
 
-					triB.a = m;
-					triB.b = c;
-					triB.c = a;
+							var vnm = face.vertexNormals[ 0 ].clone();
+							vnm.lerp( face.vertexNormals[ 1 ], 0.5 );
 
-					if ( face.vertexNormals.length === 3 ) {
+							triA.vertexNormals[ 1 ].copy( vnm );
+							triB.vertexNormals[ 0 ].copy( vnm );
 
-						var vnm = face.vertexNormals[ 1 ].clone();
-						vnm.lerp( face.vertexNormals[ 2 ], 0.5 );
+						}
 
-						triA.vertexNormals[ 2 ].copy( vnm );
+						if ( face.vertexColors.length === 3 ) {
 
-						triB.vertexNormals[ 0 ].copy( vnm );
-						triB.vertexNormals[ 1 ].copy( face.vertexNormals[ 2 ] );
-						triB.vertexNormals[ 2 ].copy( face.vertexNormals[ 0 ] );
+							var vcm = face.vertexColors[ 0 ].clone();
+							vcm.lerp( face.vertexColors[ 1 ], 0.5 );
 
-					}
+							triA.vertexColors[ 1 ].copy( vcm );
+							triB.vertexColors[ 0 ].copy( vcm );
 
-					if ( face.vertexColors.length === 3 ) {
+						}
 
-						var vcm = face.vertexColors[ 1 ].clone();
-						vcm.lerp( face.vertexColors[ 2 ], 0.5 );
+						edge = 0;
 
-						triA.vertexColors[ 2 ].copy( vcm );
+					} else if ( dbc >= dab && dbc >= dac ) {
 
-						triB.vertexColors[ 0 ].copy( vcm );
-						triB.vertexColors[ 1 ].copy( face.vertexColors[ 2 ] );
-						triB.vertexColors[ 2 ].copy( face.vertexColors[ 0 ] );
+						var vm = vb.clone();
+						vm.lerp( vc, 0.5 );
 
-					}
+						triA.a = a;
+						triA.b = b;
+						triA.c = m;
 
-					edge = 1;
+						triB.a = m;
+						triB.b = c;
+						triB.c = a;
 
-				} else {
+						if ( face.vertexNormals.length === 3 ) {
 
-					var vm = va.clone();
-					vm.lerp( vc, 0.5 );
+							var vnm = face.vertexNormals[ 1 ].clone();
+							vnm.lerp( face.vertexNormals[ 2 ], 0.5 );
 
-					triA.a = a;
-					triA.b = b;
-					triA.c = m;
+							triA.vertexNormals[ 2 ].copy( vnm );
 
-					triB.a = m;
-					triB.b = b;
-					triB.c = c;
+							triB.vertexNormals[ 0 ].copy( vnm );
+							triB.vertexNormals[ 1 ].copy( face.vertexNormals[ 2 ] );
+							triB.vertexNormals[ 2 ].copy( face.vertexNormals[ 0 ] );
 
-					if ( face.vertexNormals.length === 3 ) {
+						}
 
-						var vnm = face.vertexNormals[ 0 ].clone();
-						vnm.lerp( face.vertexNormals[ 2 ], 0.5 );
+						if ( face.vertexColors.length === 3 ) {
 
-						triA.vertexNormals[ 2 ].copy( vnm );
-						triB.vertexNormals[ 0 ].copy( vnm );
+							var vcm = face.vertexColors[ 1 ].clone();
+							vcm.lerp( face.vertexColors[ 2 ], 0.5 );
 
-					}
+							triA.vertexColors[ 2 ].copy( vcm );
+
+							triB.vertexColors[ 0 ].copy( vcm );
+							triB.vertexColors[ 1 ].copy( face.vertexColors[ 2 ] );
+							triB.vertexColors[ 2 ].copy( face.vertexColors[ 0 ] );
+
+						}
+
+						edge = 1;
+
+					} else {
+
+						var vm = va.clone();
+						vm.lerp( vc, 0.5 );
+
+						triA.a = a;
+						triA.b = b;
+						triA.c = m;
+
+						triB.a = m;
+						triB.b = b;
+						triB.c = c;
 
-					if ( face.vertexColors.length === 3 ) {
+						if ( face.vertexNormals.length === 3 ) {
 
-						var vcm = face.vertexColors[ 0 ].clone();
-						vcm.lerp( face.vertexColors[ 2 ], 0.5 );
+							var vnm = face.vertexNormals[ 0 ].clone();
+							vnm.lerp( face.vertexNormals[ 2 ], 0.5 );
 
-						triA.vertexColors[ 2 ].copy( vcm );
-						triB.vertexColors[ 0 ].copy( vcm );
+							triA.vertexNormals[ 2 ].copy( vnm );
+							triB.vertexNormals[ 0 ].copy( vnm );
+
+						}
+
+						if ( face.vertexColors.length === 3 ) {
+
+							var vcm = face.vertexColors[ 0 ].clone();
+							vcm.lerp( face.vertexColors[ 2 ], 0.5 );
+
+							triA.vertexColors[ 2 ].copy( vcm );
+							triB.vertexColors[ 0 ].copy( vcm );
+
+						}
+
+						edge = 2;
 
 					}
 
-					edge = 2;
+					faces.push( triA, triB );
+					geometry.vertices.push( vm );
 
-				}
+					for ( var j = 0, jl = geometry.faceVertexUvs.length; j < jl; j ++ ) {
 
-				faces.push( triA, triB );
-				geometry.vertices.push( vm );
+						if ( geometry.faceVertexUvs[ j ].length ) {
 
-				for ( var j = 0, jl = geometry.faceVertexUvs.length; j < jl; j ++ ) {
+							const uvs = geometry.faceVertexUvs[ j ][ i ];
 
-					if ( geometry.faceVertexUvs[ j ].length ) {
+							const uvA = uvs[ 0 ];
+							const uvB = uvs[ 1 ];
+							const uvC = uvs[ 2 ];
 
-						var uvs = geometry.faceVertexUvs[ j ][ i ];
+							// AB
 
-						var uvA = uvs[ 0 ];
-						var uvB = uvs[ 1 ];
-						var uvC = uvs[ 2 ];
+							if ( edge === 0 ) {
 
-						// AB
+								var uvM = uvA.clone();
+								uvM.lerp( uvB, 0.5 );
 
-						if ( edge === 0 ) {
+								var uvsTriA = [ uvA.clone(), uvM.clone(), uvC.clone() ];
+								var uvsTriB = [ uvM.clone(), uvB.clone(), uvC.clone() ];
 
-							var uvM = uvA.clone();
-							uvM.lerp( uvB, 0.5 );
+								// BC
 
-							var uvsTriA = [ uvA.clone(), uvM.clone(), uvC.clone() ];
-							var uvsTriB = [ uvM.clone(), uvB.clone(), uvC.clone() ];
+							} else if ( edge === 1 ) {
 
-							// BC
+								var uvM = uvB.clone();
+								uvM.lerp( uvC, 0.5 );
 
-						} else if ( edge === 1 ) {
+								var uvsTriA = [ uvA.clone(), uvB.clone(), uvM.clone() ];
+								var uvsTriB = [ uvM.clone(), uvC.clone(), uvA.clone() ];
 
-							var uvM = uvB.clone();
-							uvM.lerp( uvC, 0.5 );
+								// AC
 
-							var uvsTriA = [ uvA.clone(), uvB.clone(), uvM.clone() ];
-							var uvsTriB = [ uvM.clone(), uvC.clone(), uvA.clone() ];
+							} else {
 
-							// AC
+								var uvM = uvA.clone();
+								uvM.lerp( uvC, 0.5 );
 
-						} else {
+								var uvsTriA = [ uvA.clone(), uvB.clone(), uvM.clone() ];
+								var uvsTriB = [ uvM.clone(), uvB.clone(), uvC.clone() ];
 
-							var uvM = uvA.clone();
-							uvM.lerp( uvC, 0.5 );
+							}
 
-							var uvsTriA = [ uvA.clone(), uvB.clone(), uvM.clone() ];
-							var uvsTriB = [ uvM.clone(), uvB.clone(), uvC.clone() ];
+							faceVertexUvs[ j ].push( uvsTriA, uvsTriB );
 
 						}
 
-						faceVertexUvs[ j ].push( uvsTriA, uvsTriB );
-
 					}
 
-				}
+				} else {
 
-			} else {
+					faces.push( face );
 
-				faces.push( face );
+					for ( var j = 0, jl = geometry.faceVertexUvs.length; j < jl; j ++ ) {
 
-				for ( var j = 0, jl = geometry.faceVertexUvs.length; j < jl; j ++ ) {
+						faceVertexUvs[ j ].push( geometry.faceVertexUvs[ j ][ i ] );
 
-					faceVertexUvs[ j ].push( geometry.faceVertexUvs[ j ][ i ] );
+					}
 
 				}
 
@@ -230,10 +262,20 @@ TessellateModifier.prototype.modify = function ( geometry ) {
 
 		}
 
+		geometry.faces = faces;
+		geometry.faceVertexUvs = faceVertexUvs;
+
 	}
 
-	geometry.faces = faces;
-	geometry.faceVertexUvs = faceVertexUvs;
+	if ( isBufferGeometry ) {
+
+		return new BufferGeometry().fromGeometry( geometry );
+
+	} else {
+
+		return geometry;
+
+	}
 
 };
 

+ 5 - 0
examples/jsm/nodes/materials/NodeMaterial.js

@@ -68,6 +68,11 @@ NodeMaterial.prototype.onBeforeCompile = function ( shader, renderer ) {
 	shader.vertexShader = this.vertexShader;
 	shader.fragmentShader = this.fragmentShader;
 
+	shader.extensionDerivatives = ( this.extensions.derivatives === true );
+	shader.extensionFragDepth = ( this.extensions.fragDepth === true );
+	shader.extensionDrawBuffers = ( this.extensions.drawBuffers === true );
+	shader.extensionShaderTextureLOD = ( this.extensions.shaderTextureLOD === true );
+
 };
 
 NodeMaterial.prototype.customProgramCacheKey = function () {

+ 95 - 0
examples/jsm/renderers/webgpu/ShaderLib.js

@@ -0,0 +1,95 @@
+const ShaderLib = {
+	meshBasic: {
+		vertexShader: `#version 450
+
+		layout(location = 0) in vec3 position;
+		layout(location = 1) in vec2 uv;
+
+		layout(location = 0) out vec2 vUv;
+
+		layout(set = 0, binding = 0) uniform ModelUniforms {
+			mat4 modelMatrix;
+			mat4 modelViewMatrix;
+			mat3 normalMatrix;
+		} modelUniforms;
+
+		layout(set = 0, binding = 1) uniform CameraUniforms {
+			mat4 projectionMatrix;
+			mat4 viewMatrix;
+		} cameraUniforms;
+
+		void main(){
+			vUv = uv;
+			gl_Position = cameraUniforms.projectionMatrix * modelUniforms.modelViewMatrix * vec4( position, 1.0 );
+		}`,
+		fragmentShader: `#version 450
+		layout(set = 0, binding = 2) uniform OpacityUniforms {
+			float opacity;
+		} opacityUniforms;
+
+		layout(set = 0, binding = 3) uniform sampler mySampler;
+		layout(set = 0, binding = 4) uniform texture2D myTexture;
+
+		layout(location = 0) in vec2 vUv;
+		layout(location = 0) out vec4 outColor;
+
+		void main() {
+			outColor = texture( sampler2D( myTexture, mySampler ), vUv );
+			outColor.a *= opacityUniforms.opacity;
+		}`
+	},
+	pointsBasic: {
+		vertexShader: `#version 450
+
+		layout(location = 0) in vec3 position;
+
+		layout(set = 0, binding = 0) uniform ModelUniforms {
+			mat4 modelMatrix;
+			mat4 modelViewMatrix;
+		} modelUniforms;
+
+		layout(set = 0, binding = 1) uniform CameraUniforms {
+			mat4 projectionMatrix;
+			mat4 viewMatrix;
+		} cameraUniforms;
+
+		void main(){
+			gl_Position = cameraUniforms.projectionMatrix * modelUniforms.modelViewMatrix * vec4( position, 1.0 );
+		}`,
+		fragmentShader: `#version 450
+
+		layout(location = 0) out vec4 outColor;
+
+		void main() {
+			outColor = vec4( 1.0, 0.0, 0.0, 1.0 );
+		}`
+	},
+	lineBasic: {
+		vertexShader: `#version 450
+
+		layout(location = 0) in vec3 position;
+
+		layout(set = 0, binding = 0) uniform ModelUniforms {
+			mat4 modelMatrix;
+			mat4 modelViewMatrix;
+		} modelUniforms;
+
+		layout(set = 0, binding = 1) uniform CameraUniforms {
+			mat4 projectionMatrix;
+			mat4 viewMatrix;
+		} cameraUniforms;
+
+		void main(){
+			gl_Position = cameraUniforms.projectionMatrix * modelUniforms.modelViewMatrix * vec4( position, 1.0 );
+		}`,
+		fragmentShader: `#version 450
+
+		layout(location = 0) out vec4 outColor;
+
+		void main() {
+			outColor = vec4( 1.0, 0.0, 0.0, 1.0 );
+		}`
+	}
+};
+
+export default ShaderLib;

+ 5 - 97
examples/jsm/renderers/webgpu/WebGPURenderPipelines.js

@@ -9,6 +9,8 @@ import {
 	ZeroFactor, OneFactor, SrcColorFactor, OneMinusSrcColorFactor, SrcAlphaFactor, OneMinusSrcAlphaFactor, DstAlphaFactor, OneMinusDstAlphaFactor, DstColorFactor, OneMinusDstColorFactor, SrcAlphaSaturateFactor
 } from '../../../../build/three.module.js';
 
+import ShaderLib from './ShaderLib.js';
+
 class WebGPURenderPipelines {
 
 	constructor( renderer, properties, device, glslang, sampleCount ) {
@@ -57,15 +59,15 @@ class WebGPURenderPipelines {
 
 			if ( material.isMeshBasicMaterial ) {
 
-				shader = ShaderLib.mesh_basic;
+				shader = ShaderLib.meshBasic;
 
 			} else if ( material.isPointsMaterial ) {
 
-				shader = ShaderLib.points_basic;
+				shader = ShaderLib.pointsBasic;
 
 			} else if ( material.isLineBasicMaterial ) {
 
-				shader = ShaderLib.line_basic;
+				shader = ShaderLib.lineBasic;
 
 			} else {
 
@@ -790,98 +792,4 @@ class WebGPURenderPipelines {
 
 }
 
-const ShaderLib = {
-	mesh_basic: {
-		vertexShader: `#version 450
-
-		layout(location = 0) in vec3 position;
-		layout(location = 1) in vec2 uv;
-
-		layout(location = 0) out vec2 vUv;
-
-		layout(set = 0, binding = 0) uniform ModelUniforms {
-			mat4 modelMatrix;
-			mat4 modelViewMatrix;
-			mat3 normalMatrix;
-		} modelUniforms;
-
-		layout(set = 0, binding = 1) uniform CameraUniforms {
-			mat4 projectionMatrix;
-			mat4 viewMatrix;
-		} cameraUniforms;
-
-		void main(){
-			vUv = uv;
-			gl_Position = cameraUniforms.projectionMatrix * modelUniforms.modelViewMatrix * vec4( position, 1.0 );
-		}`,
-		fragmentShader: `#version 450
-		layout(set = 0, binding = 2) uniform OpacityUniforms {
-			float opacity;
-		} opacityUniforms;
-
-		layout(set = 0, binding = 3) uniform sampler mySampler;
-		layout(set = 0, binding = 4) uniform texture2D myTexture;
-
-		layout(location = 0) in vec2 vUv;
-		layout(location = 0) out vec4 outColor;
-
-		void main() {
-			outColor = texture( sampler2D( myTexture, mySampler ), vUv );
-			outColor.a *= opacityUniforms.opacity;
-		}`
-	},
-	points_basic: {
-		vertexShader: `#version 450
-
-		layout(location = 0) in vec3 position;
-
-		layout(set = 0, binding = 0) uniform ModelUniforms {
-			mat4 modelMatrix;
-			mat4 modelViewMatrix;
-		} modelUniforms;
-
-		layout(set = 0, binding = 1) uniform CameraUniforms {
-			mat4 projectionMatrix;
-			mat4 viewMatrix;
-		} cameraUniforms;
-
-		void main(){
-			gl_Position = cameraUniforms.projectionMatrix * modelUniforms.modelViewMatrix * vec4( position, 1.0 );
-		}`,
-		fragmentShader: `#version 450
-
-		layout(location = 0) out vec4 outColor;
-
-		void main() {
-			outColor = vec4( 1.0, 0.0, 0.0, 1.0 );
-		}`
-	},
-	line_basic: {
-		vertexShader: `#version 450
-
-		layout(location = 0) in vec3 position;
-
-		layout(set = 0, binding = 0) uniform ModelUniforms {
-			mat4 modelMatrix;
-			mat4 modelViewMatrix;
-		} modelUniforms;
-
-		layout(set = 0, binding = 1) uniform CameraUniforms {
-			mat4 projectionMatrix;
-			mat4 viewMatrix;
-		} cameraUniforms;
-
-		void main(){
-			gl_Position = cameraUniforms.projectionMatrix * modelUniforms.modelViewMatrix * vec4( position, 1.0 );
-		}`,
-		fragmentShader: `#version 450
-
-		layout(location = 0) out vec4 outColor;
-
-		void main() {
-			outColor = vec4( 1.0, 0.0, 0.0, 1.0 );
-		}`
-	}
-};
-
 export default WebGPURenderPipelines;

+ 9 - 1
examples/jsm/renderers/webgpu/WebGPURenderer.js

@@ -66,7 +66,7 @@ class WebGPURenderer {
 
 		// public
 
-		this.domElement = ( parameters.canvas !== undefined ) ? parameters.canvas : document.createElementNS( 'http://www.w3.org/1999/xhtml', 'canvas' );
+		this.domElement = ( parameters.canvas !== undefined ) ? parameters.canvas : this._createCanvasElement();
 
 		this.autoClear = true;
 		this.autoClearColor = true;
@@ -873,6 +873,14 @@ class WebGPURenderer {
 
 	}
 
+	_createCanvasElement() {
+
+		const canvas = document.createElementNS( 'http://www.w3.org/1999/xhtml', 'canvas' );
+		canvas.style.display = 'block';
+		return canvas;
+
+	}
+
 }
 
 export default WebGPURenderer;

+ 8 - 0
examples/models/svg/zero-radius.svg

@@ -0,0 +1,8 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="930.29" height="802.11" viewBox="0 0 930.29 802.11">
+  <path
+    d="M731.61,544H889.45A10.5,10.5,0,0,1,900,554.5v16.13a0,0,0,0,1,0,0H713.61a0,0,0,0,1,0,0V562A18,18,0,0,1,731.61,544Z"
+   fill="#40414d" />
+  <path
+    d="M906.69,385.69h8.06a0,0,0,0,1,0,0V563.63a7,7,0,0,1-7,7H901a7,7,0,0,1-7-7V398.35a12.66,12.66,0,0,1,12.66-12.66Z"
+    fill="#40414d" />
+</svg>

+ 203 - 0
examples/models/xyz/helix_201.xyz

@@ -0,0 +1,203 @@
+# helix_201.xyz
+#
+  0.3517846     -0.7869986      -2.873479    
+  0.5057634     -0.7079139      -2.871073    
+  0.6422613     -0.5988617      -2.868600    
+  0.7554799     -0.4636500      -2.866056    
+  0.8404377     -0.3072074      -2.863440    
+  0.8931900     -0.1353982      -2.860748    
+  0.9110007      4.5183171E-02  -2.857978    
+  0.8924776      0.2274645      -2.855126    
+  0.8376509      0.4041732      -2.852190    
+  0.7480025      0.5681100      -2.849165    
+  0.6264315      0.7124491      -2.846050    
+  0.4771712      0.8310061      -2.842839    
+  0.3056472      0.9185020      -2.839530    
+  0.1182791      0.9707931      -2.836119    
+ -7.7742465E-02  0.9850534      -2.832601    
+ -0.2747397      0.9599173      -2.828971    
+ -0.4648297      0.8955631      -2.825226    
+ -0.6402391      0.7937393      -2.821360    
+ -0.7936199      0.6577247      -2.817368    
+ -0.9183395      0.4922375      -2.813246    
+  -1.008770      0.3032688      -2.808988    
+  -1.060525      9.7884014E-02  -2.804586    
+  -1.070667     -0.1160435      -2.800037    
+  -1.037852     -0.3301237      -2.795332    
+ -0.9624209     -0.5357682      -2.790466    
+ -0.8464230     -0.7245325      -2.785430    
+ -0.6935734     -0.8884509      -2.780218    
+ -0.5091438      -1.020364      -2.774821    
+ -0.2997888      -1.114221      -2.769231    
+ -7.3318549E-02  -1.165346      -2.763439    
+  0.1615903      -1.170658      -2.757435    
+  0.3957134      -1.128828      -2.751210    
+  0.6196325      -1.040377      -2.744753    
+  0.8241052     -0.9076963      -2.738054    
+   1.000432     -0.7350051      -2.731100    
+   1.140819     -0.5282233      -2.723878    
+   1.238709     -0.2947820      -2.716377    
+   1.289071     -4.3367516E-02  -2.708581    
+   1.288640      0.2163926      -2.700478    
+   1.236092      0.4742811      -2.692050    
+   1.132149      0.7198882      -2.683281    
+  0.9796010      0.9430137      -2.674155    
+  0.7832562       1.134079      -2.664653    
+  0.5498004       1.284522      -2.654754    
+  0.2875895       1.387166      -2.644440    
+  6.3577644E-03   1.436542      -2.633687    
+ -0.2831245       1.429154      -2.622472    
+ -0.5694501       1.363673      -2.610770    
+ -0.8410097       1.241050      -2.598557    
+  -1.086441       1.064551      -2.585803    
+  -1.295096      0.8396897      -2.572479    
+  -1.457477      0.5740758      -2.558554    
+  -1.565650      0.2771796      -2.543995    
+  -1.613611     -3.9990790E-02  -2.528766    
+  -1.597577     -0.3652730      -2.512832    
+  -1.516212     -0.6858066      -2.496151    
+  -1.370756     -0.9885159      -2.478682    
+  -1.165054      -1.260625      -2.460381    
+ -0.9054906      -1.490177      -2.441200    
+ -0.6008227      -1.666533      -2.421091    
+ -0.2619001      -1.780845      -2.400000    
+  9.8691247E-02  -1.826468      -2.377872    
+  0.4670753      -1.799298      -2.354648    
+  0.8285937      -1.698024      -2.330267    
+   1.168371      -1.524281      -2.304664    
+   1.471899      -1.282684      -2.277770    
+   1.725636     -0.9807486      -2.249514    
+   1.917587     -0.6286960      -2.219820    
+   2.037840     -0.2391386      -2.188612    
+   2.079050      0.1733468      -2.155806    
+   2.036829      0.5927297      -2.121320    
+   1.910035       1.002130      -2.085066    
+   1.700944       1.384473      -2.046955    
+   1.415291       1.723174      -2.006894    
+   1.062158       2.002851      -1.964792    
+  0.6537460       2.209998      -1.920553    
+  0.2049761       2.333622      -1.874085    
+ -0.2670151       2.365798      -1.825295    
+ -0.7435041       2.302130      -1.774091    
+  -1.204912       2.142068      -1.720387    
+  -1.631593       1.889093      -1.664101    
+  -2.004674       1.550727      -1.605156    
+  -2.306892       1.138374      -1.543487    
+  -2.523408      0.6669796      -1.479038    
+  -2.642545      0.1545199      -1.411765    
+  -2.656429     -0.3786647      -1.341641    
+  -2.561478     -0.9106821      -1.268656    
+  -2.358729      -1.419004      -1.192822    
+  -2.053959      -1.881455      -1.114172    
+  -1.657589      -2.277234      -1.032764    
+  -1.184375      -2.587906     -0.9486833    
+ -0.6528603      -2.798331     -0.8620437    
+ -8.4640868E-02  -2.897469     -0.7729880    
+  0.4965631      -2.879015     -0.6816889    
+   1.065963      -2.741819     -0.5883484    
+   1.598853      -2.490066     -0.4931970    
+   2.071785      -2.133190     -0.3964912    
+   2.463719      -1.685521     -0.2985111    
+   2.757063      -1.165668     -0.1995571    
+   2.938568     -0.5956771     -9.9944495E-02
+   3.000000      0.0000000E+00  0.0000000E+00
+   2.938568      0.5956771      9.9944495E-02
+   2.757063       1.165668      0.1995571    
+   2.463719       1.685521      0.2985111    
+   2.071785       2.133190      0.3964912    
+   1.598853       2.490066      0.4931970    
+   1.065963       2.741819      0.5883484    
+  0.4965631       2.879015      0.6816889    
+ -8.4640868E-02   2.897469      0.7729880    
+ -0.6528603       2.798331      0.8620437    
+  -1.184375       2.587906      0.9486833    
+  -1.657589       2.277234       1.032764    
+  -2.053959       1.881455       1.114172    
+  -2.358729       1.419004       1.192822    
+  -2.561478      0.9106821       1.268656    
+  -2.656429      0.3786647       1.341641    
+  -2.642545     -0.1545199       1.411765    
+  -2.523408     -0.6669796       1.479038    
+  -2.306892      -1.138374       1.543487    
+  -2.004674      -1.550727       1.605156    
+  -1.631593      -1.889093       1.664101    
+  -1.204912      -2.142068       1.720387    
+ -0.7435041      -2.302130       1.774091    
+ -0.2670151      -2.365798       1.825295    
+  0.2049761      -2.333622       1.874085    
+  0.6537460      -2.209998       1.920553    
+   1.062158      -2.002851       1.964792    
+   1.415291      -1.723174       2.006894    
+   1.700944      -1.384473       2.046955    
+   1.910035      -1.002130       2.085066    
+   2.036829     -0.5927297       2.121320    
+   2.079050     -0.1733468       2.155806    
+   2.037840      0.2391386       2.188612    
+   1.917587      0.6286960       2.219820    
+   1.725636      0.9807486       2.249514    
+   1.471899       1.282684       2.277770    
+   1.168371       1.524281       2.304664    
+  0.8285937       1.698024       2.330267    
+  0.4670753       1.799298       2.354648    
+  9.8691247E-02   1.826468       2.377872    
+ -0.2619001       1.780845       2.400000    
+ -0.6008227       1.666533       2.421091    
+ -0.9054906       1.490177       2.441200    
+  -1.165054       1.260625       2.460381    
+  -1.370756      0.9885159       2.478682    
+  -1.516212      0.6858066       2.496151    
+  -1.597577      0.3652730       2.512832    
+  -1.613611      3.9990790E-02   2.528766    
+  -1.565650     -0.2771796       2.543995    
+  -1.457477     -0.5740758       2.558554    
+  -1.295096     -0.8396897       2.572479    
+  -1.086441      -1.064551       2.585803    
+ -0.8410097      -1.241050       2.598557    
+ -0.5694501      -1.363673       2.610770    
+ -0.2831245      -1.429154       2.622472    
+  6.3577644E-03  -1.436542       2.633687    
+  0.2875895      -1.387166       2.644440    
+  0.5498004      -1.284522       2.654754    
+  0.7832562      -1.134079       2.664653    
+  0.9796010     -0.9430137       2.674155    
+   1.132149     -0.7198882       2.683281    
+   1.236092     -0.4742811       2.692050    
+   1.288640     -0.2163926       2.700478    
+   1.289071      4.3367516E-02   2.708581    
+   1.238709      0.2947820       2.716377    
+   1.140819      0.5282233       2.723878    
+   1.000432      0.7350051       2.731100    
+  0.8241052      0.9076963       2.738054    
+  0.6196325       1.040377       2.744753    
+  0.3957134       1.128828       2.751210    
+  0.1615903       1.170658       2.757435    
+ -7.3318549E-02   1.165346       2.763439    
+ -0.2997888       1.114221       2.769231    
+ -0.5091438       1.020364       2.774821    
+ -0.6935734      0.8884509       2.780218    
+ -0.8464230      0.7245325       2.785430    
+ -0.9624209      0.5357682       2.790466    
+  -1.037852      0.3301237       2.795332    
+  -1.070667      0.1160435       2.800037    
+  -1.060525     -9.7884014E-02   2.804586    
+  -1.008770     -0.3032688       2.808988    
+ -0.9183395     -0.4922375       2.813246    
+ -0.7936199     -0.6577247       2.817368    
+ -0.6402391     -0.7937393       2.821360    
+ -0.4648297     -0.8955631       2.825226    
+ -0.2747397     -0.9599173       2.828971    
+ -7.7742465E-02 -0.9850534       2.832601    
+  0.1182791     -0.9707931       2.836119    
+  0.3056472     -0.9185020       2.839530    
+  0.4771712     -0.8310061       2.842839    
+  0.6264315     -0.7124491       2.846050    
+  0.7480025     -0.5681100       2.849165    
+  0.8376509     -0.4041732       2.852190    
+  0.8924776     -0.2274645       2.855126    
+  0.9110007     -4.5183171E-02   2.857978    
+  0.8931900      0.1353982       2.860748    
+  0.8404377      0.3072074       2.863440    
+  0.7554799      0.4636500       2.866056    
+  0.6422613      0.5988617       2.868600    
+  0.5057634      0.7079139       2.871073    
+  0.3517846      0.7869986       2.873479    

BIN
examples/screenshots/webgl_gpgpu_birds_gltf.jpg


BIN
examples/screenshots/webgl_loader_xyz.jpg


+ 3 - 1
examples/webgl_gpgpu_birds.html

@@ -184,7 +184,9 @@
 
 							// Attraction / Cohesion - move closer
 							float threshDelta = 1.0 - alignmentThresh;
-							float adjustedPercent = ( percent - alignmentThresh ) / threshDelta;
+							float adjustedPercent;
+							if( threshDelta == 0. ) adjustedPercent = 1.;
+							else adjustedPercent = ( percent - alignmentThresh ) / threshDelta;
 
 							f = ( 0.5 - ( cos( adjustedPercent * PI_2 ) * -0.5 + 0.5 ) ) * delta;
 

+ 8 - 6
examples/webgl_gpgpu_birds_gltf.html

@@ -179,7 +179,9 @@
 
 							// Attraction / Cohesion - move closer
 							float threshDelta = 1.0 - alignmentThresh;
-							float adjustedPercent = ( percent - alignmentThresh ) / threshDelta;
+							float adjustedPercent;
+							if( threshDelta == 0. ) adjustedPercent = 1.;
+							else adjustedPercent = ( percent - alignmentThresh ) / threshDelta;
 
 							f = ( 0.5 - ( cos( adjustedPercent * PI_2 ) * -0.5 + 0.5 ) ) * delta;
 
@@ -422,7 +424,7 @@
 				gui.add( effectController, "count", 0, BIRDS, 1 ).onChange( valuesChanger );
 				gui.close();
 
-				initBirds();
+				initBirds( effectController );
 
 			}
 
@@ -483,7 +485,7 @@
 
 			}
 
-			function initBirds() {
+			function initBirds( effectController ) {
 
 				const geometry = BirdGeometry;
 
@@ -500,7 +502,7 @@
 					shader.uniforms.textureVelocity = { value: null };
 					shader.uniforms.textureAnimation = { value: textureAnimation };
 					shader.uniforms.time = { value: 1.0 };
-					shader.uniforms.size = { value: 0.1 };
+					shader.uniforms.size = { value: effectController.size };
 					shader.uniforms.delta = { value: 0.0 };
 
 					let token = '#define STANDARD';
@@ -525,7 +527,7 @@
 
 						vec3 pos = tmpPos.xyz;
 						vec3 velocity = normalize(texture2D( textureVelocity, reference.xy ).xyz);
-						vec3 aniPos = texture2D( textureAnimation, vec2( reference.z, mod( ( time + seeds.x ) * ( ( 0.0004 + seeds.y / 10000.0) + normalize( velocity ) / 20000.0 ), reference.w ) ) ).xyz;
+						vec3 aniPos = texture2D( textureAnimation, vec2( reference.z, mod( time + ( seeds.x ) * ( ( 0.0004 + seeds.y / 10000.0) + normalize( velocity ) / 20000.0 ), reference.w ) ) ).xyz;
 						vec3 newPosition = position;
 
 						newPosition = mat3( modelMatrix ) * ( newPosition + aniPos );
@@ -649,7 +651,7 @@
 				positionUniforms[ "delta" ].value = delta;
 				velocityUniforms[ "time" ].value = now;
 				velocityUniforms[ "delta" ].value = delta;
-				if ( materialShader ) materialShader.uniforms[ "time" ].value = now;
+				if ( materialShader ) materialShader.uniforms[ "time" ].value = now / 1000;
 				if ( materialShader ) materialShader.uniforms[ "delta" ].value = delta;
 
 				velocityUniforms[ "predator" ].value.set( 0.5 * mouseX / windowHalfX, - 0.5 * mouseY / windowHalfY, 0 );

+ 2 - 2
examples/webgl_loader_svg.html

@@ -104,8 +104,8 @@
 					"Defs": 'models/svg/tests/testDefs/Svg-defs.svg',
 					"Defs2": 'models/svg/tests/testDefs/Svg-defs2.svg',
 					"Defs3": 'models/svg/tests/testDefs/Wave-defs.svg',
-					"Defs4": 'models/svg/tests/testDefs/defs4.svg'
-
+					"Defs4": 'models/svg/tests/testDefs/defs4.svg',
+					"Zero Radius": 'models/svg/zero-radius.svg'
 
 				} ).name( 'SVG File' ).onChange( update );
 

+ 1 - 1
examples/webgl_loader_texture_lottie.html

@@ -44,7 +44,7 @@
 
 					// texture = new THREE.TextureLoader().load( 'textures/uv_grid_directx.jpg' );
 
-					const geometry = new RoundedBoxBufferGeometry( 1, 1, 1, 15, 0.2 );
+					const geometry = new RoundedBoxBufferGeometry( 1, 1, 1, 7, 0.2 );
 					const material = new THREE.MeshStandardMaterial( { roughness: 0.1, map: texture } );
 					mesh = new THREE.Mesh( geometry, material );
 					scene.add( mesh );

+ 94 - 0
examples/webgl_loader_xyz.html

@@ -0,0 +1,94 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<title>three.js webgl - loaders - XYZ</title>
+		<meta charset="utf-8">
+		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
+		<link type="text/css" rel="stylesheet" href="main.css">
+	</head>
+
+	<body>
+		<div id="info">
+		<a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> - XYZ loader<br/>
+		asset from <a href="https://people.math.sc.edu/Burkardt/data/xyz/xyz.html" target="_blank" rel="noopener">people.math.sc.edu</a> via GNU LGPL
+		</div>
+
+		<script type="module">
+
+			import * as THREE from '../build/three.module.js';
+
+			import { XYZLoader } from './jsm/loaders/XYZLoader.js';
+
+			let camera, scene, renderer, clock;
+
+			let points;
+
+			init();
+			animate();
+
+			function init() {
+
+				camera = new THREE.PerspectiveCamera( 50, window.innerWidth / window.innerHeight, 0.1, 100 );
+				camera.position.set( 10, 7, 10 );
+
+				scene = new THREE.Scene();
+				scene.add( camera );
+				camera.lookAt( scene.position );
+
+				clock = new THREE.Clock();
+
+				const loader = new XYZLoader();
+				loader.load( 'models/xyz/helix_201.xyz', function ( geometry ) {
+
+					geometry.center();
+
+					const material = new THREE.PointsMaterial( { size: 0.1 } );
+
+					points = new THREE.Points( geometry, material );
+					scene.add( points );
+
+				} );
+
+				//
+
+				renderer = new THREE.WebGLRenderer();
+				renderer.setPixelRatio( window.devicePixelRatio );
+				renderer.setSize( window.innerWidth, window.innerHeight );
+				document.body.appendChild( renderer.domElement );
+
+				//
+
+				window.addEventListener( 'resize', onWindowResize, false );
+
+			}
+
+			function onWindowResize() {
+
+				camera.aspect = window.innerWidth / window.innerHeight;
+				camera.updateProjectionMatrix();
+
+				renderer.setSize( window.innerWidth, window.innerHeight );
+
+			}
+
+			function animate() {
+
+				requestAnimationFrame( animate );
+
+				const delta = clock.getDelta();
+
+				if ( points ) {
+
+					points.rotation.x += delta * 0.2;
+					points.rotation.y += delta * 0.5;
+
+				}
+
+				renderer.render( scene, camera );
+
+			}
+
+		</script>
+
+	</body>
+</html>

+ 14 - 19
examples/webgl_modifier_curve.html

@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <html lang="en">
 	<head>
-		<title>three.js webgl - animation - groups</title>
+		<title>three.js webgl - curve modifier</title>
 		<meta charset="utf-8" />
 		<meta
 			name="viewport"
@@ -11,10 +11,7 @@
 	</head>
 	<body>
 		<div id="info">
-			<a href="https://threejs.org" target="_blank" rel="noopener"
-				>three.js</a
-			>
-			webgl - curve modifier
+			<a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> webgl - curve modifier
 		</div>
 
 		<script type="module">
@@ -26,6 +23,7 @@
 			const ACTION_SELECT = 1,
 				ACTION_NONE = 0;
 			const curveHandles = [];
+			const mouse = new THREE.Vector2();
 
 			let stats;
 			let scene,
@@ -33,7 +31,6 @@
 				renderer,
 				rayCaster,
 				control,
-				mouse,
 				flow,
 				action = ACTION_NONE;
 
@@ -60,8 +57,9 @@
 					{ x: - 1, y: 0, z: - 1 },
 				];
 
-				const boxGeometry = new THREE.BoxGeometry( 0.1, 0.1, 0.1 );
+				const boxGeometry = new THREE.BoxBufferGeometry( 0.1, 0.1, 0.1 );
 				const boxMaterial = new THREE.MeshBasicMaterial( 0x99ff99 );
+
 				for ( const handlePos of initialPoints ) {
 
 					const handle = new THREE.Mesh( boxGeometry, boxMaterial );
@@ -137,18 +135,7 @@
 				renderer.setSize( window.innerWidth, window.innerHeight );
 				document.body.appendChild( renderer.domElement );
 
-				mouse = new THREE.Vector2();
-				renderer.domElement.addEventListener(
-					"click",
-					function ( event ) {
-
-						action = ACTION_SELECT;
-						mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
-						mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
-
-					},
-					false
-				);
+				renderer.domElement.addEventListener( 'pointerdown', onPointerDown, false );
 
 				rayCaster = new THREE.Raycaster();
 				control = new TransformControls( camera, renderer.domElement );
@@ -180,6 +167,14 @@
 
 			}
 
+			function onPointerDown( event ) {
+
+				action = ACTION_SELECT;
+				mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
+				mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
+
+			}
+
 			function animate() {
 
 				requestAnimationFrame( animate );

+ 13 - 19
examples/webgl_modifier_curve_instanced.html

@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <html lang="en">
 	<head>
-		<title>three.js webgl - animation - groups</title>
+		<title>three.js webgl - curve modifier - instanced</title>
 		<meta charset="utf-8" />
 		<meta
 			name="viewport"
@@ -11,10 +11,7 @@
 	</head>
 	<body>
 		<div id="info">
-			<a href="https://threejs.org" target="_blank" rel="noopener"
-				>three.js</a
-			>
-			webgl - curve modifier - instanced
+			<a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> webgl - curve modifier - instanced
 		</div>
 
 		<script type="module">
@@ -26,6 +23,7 @@
 			const ACTION_SELECT = 1,
 				ACTION_NONE = 0;
 			const curveHandles = [];
+			const mouse = new THREE.Vector2();
 
 			let stats;
 			let scene,
@@ -33,7 +31,6 @@
 				renderer,
 				rayCaster,
 				control,
-				mouse,
 				flow,
 				action = ACTION_NONE;
 
@@ -53,7 +50,7 @@
 				camera.position.set( 2, 2, 4 );
 				camera.lookAt( scene.position );
 
-				const boxGeometry = new THREE.BoxGeometry( 0.1, 0.1, 0.1 );
+				const boxGeometry = new THREE.BoxBufferGeometry( 0.1, 0.1, 0.1 );
 				const boxMaterial = new THREE.MeshBasicMaterial( 0x99ff99 );
 
 				const curves = [[
@@ -164,18 +161,7 @@
 				renderer.setSize( window.innerWidth, window.innerHeight );
 				document.body.appendChild( renderer.domElement );
 
-				mouse = new THREE.Vector2();
-				renderer.domElement.addEventListener(
-					"click",
-					function ( event ) {
-
-						action = ACTION_SELECT;
-						mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
-						mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
-
-					},
-					false
-				);
+				renderer.domElement.addEventListener( 'pointerdown', onPointerDown, false );
 
 				rayCaster = new THREE.Raycaster();
 				control = new TransformControls( camera, renderer.domElement );
@@ -211,6 +197,14 @@
 
 			}
 
+			function onPointerDown( event ) {
+
+				action = ACTION_SELECT;
+				mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
+				mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
+
+			}
+
 			function animate() {
 
 				requestAnimationFrame( animate );

+ 2 - 6
examples/webgl_modifier_tessellation.html

@@ -105,13 +105,9 @@
 
 				geometry.center();
 
-				const tessellateModifier = new TessellateModifier( 8 );
+				const tessellateModifier = new TessellateModifier( 8, 6 );
 
-				for ( let i = 0; i < 6; i ++ ) {
-
-					tessellateModifier.modify( geometry );
-
-				}
+				geometry = tessellateModifier.modify( geometry );
 
 				//
 

+ 2 - 2
src/core/Geometry.d.ts

@@ -242,10 +242,10 @@ export class Geometry extends EventDispatcher {
 	mergeMesh( mesh: Mesh ): void;
 
 	/**
-	 * Checks for duplicate vertices using hashmap.
+	 * Checks for duplicate vertices using hashmap for specified number of decimal points, e.g. 4 for epsilon of 0.0001
 	 * Duplicated vertices are removed and faces' vertices are updated.
 	 */
-	mergeVertices(): number;
+	mergeVertices( precisionPoints?: number ): number;
 
 	setFromPoints( points: Array<Vector2> | Array<Vector3> ): this;
 

+ 1 - 2
src/core/Geometry.js

@@ -769,12 +769,11 @@ Geometry.prototype = Object.assign( Object.create( EventDispatcher.prototype ),
 	 * and faces' vertices are updated.
 	 */
 
-	mergeVertices: function () {
+	mergeVertices: function ( precisionPoints = 4 ) {
 
 		const verticesMap = {}; // Hashmap for looking up vertices by position coordinates (and making sure they are unique)
 		const unique = [], changes = [];
 
-		const precisionPoints = 4; // number of decimal points, e.g. 4 for epsilon of 0.0001
 		const precision = Math.pow( 10, precisionPoints );
 
 		for ( let i = 0, il = this.vertices.length; i < il; i ++ ) {

+ 7 - 0
src/math/Matrix3.d.ts

@@ -1,6 +1,12 @@
 import { Matrix4 } from './Matrix4';
 import { Vector3 } from './Vector3';
 
+type Matrix3Tuple = [
+	number, number, number,
+	number, number, number,
+	number, number, number,
+];
+
 /**
  * ( interface Matrix<T> )
  */
@@ -125,6 +131,7 @@ export class Matrix3 implements Matrix {
 	 * @return The created or provided array.
 	 */
 	toArray( array?: number[], offset?: number ): number[];
+	toArray( array?: Matrix3Tuple, offset?: 0 ): Matrix3Tuple;
 
 	/**
 	 * Copies he values of this matrix into the provided array-like.

+ 9 - 0
src/math/Matrix4.d.ts

@@ -2,6 +2,14 @@ import { Vector3 } from './Vector3';
 import { Euler } from './Euler';
 import { Quaternion } from './Quaternion';
 import { Matrix } from './Matrix3';
+
+type Matrix4Tuple = [
+	number, number, number, number,
+	number, number, number, number,
+	number, number, number, number,
+	number, number, number, number,
+];
+
 /**
  * A 4x4 Matrix.
  *
@@ -235,6 +243,7 @@ export class Matrix4 implements Matrix {
 	 * @return The created or provided array.
 	 */
 	toArray( array?: number[], offset?: number ): number[];
+	toArray( array?: Matrix4Tuple, offset?: 0 ): Matrix4Tuple;
 
 	/**
 	 * Copies he values of this matrix into the provided array-like.

+ 3 - 0
src/math/Vector2.d.ts

@@ -1,6 +1,8 @@
 import { Matrix3 } from './Matrix3';
 import { BufferAttribute } from './../core/BufferAttribute';
 
+type Vector2tuple = [number, number];
+
 /**
  * ( interface Vector<T> )
  *
@@ -427,6 +429,7 @@ export class Vector2 implements Vector {
 	 * @return The created or provided array.
 	 */
 	toArray( array?: number[], offset?: number ): number[];
+	toArray( array?: Vector2tuple, offset?: 0 ): Vector2tuple;
 
 	/**
 	 * Copies x and y into the provided array-like.

+ 4 - 0
src/math/Vector3.d.ts

@@ -8,6 +8,9 @@ import { Cylindrical } from './Cylindrical';
 import { BufferAttribute } from './../core/BufferAttribute';
 import { InterleavedBufferAttribute } from './../core/InterleavedBufferAttribute';
 import { Vector } from './Vector2';
+
+type Vector3Tuple = [number, number, number];
+
 /**
  * 3D vector.
  *
@@ -284,6 +287,7 @@ export class Vector3 implements Vector {
 	 * @return The created or provided array.
 	 */
 	toArray( array?: number[], offset?: number ): number[];
+	toArray( array?: Vector3Tuple, offset?: 0 ): Vector3Tuple;
 
 	/**
 	 * Copies x, y and z into the provided array-like.

+ 3 - 0
src/math/Vector4.d.ts

@@ -4,6 +4,8 @@ import { Matrix3 } from './Matrix3';
 import { BufferAttribute } from './../core/BufferAttribute';
 import { Vector } from './Vector2';
 
+type Vector4Tuple = [number, number, number, number];
+
 /**
  * 4D vector.
  *
@@ -211,6 +213,7 @@ export class Vector4 implements Vector {
 	 * @return The created or provided array.
 	 */
 	toArray( array?: number[], offset?: number ): number[];
+	toArray( array?: Vector4Tuple, offset?: 0 ): Vector4Tuple;
 
 	/**
 	 * Copies x, y, z and w into the provided array-like.

+ 3 - 1
src/objects/InstancedMesh.d.ts

@@ -18,11 +18,13 @@ export class InstancedMesh <
 	);
 
 	count: number;
+	instanceColor: null | BufferAttribute;
 	instanceMatrix: BufferAttribute;
 	readonly isInstancedMesh: true;
 
+	getColorAt( index: number, color: Color ): void;
 	getMatrixAt( index: number, matrix: Matrix4 ): void;
-	setMatrixAt( index: number, matrix: Matrix4 ): void;
 	setColorAt( index: number, color: Color ): void;
+	setMatrixAt( index: number, matrix: Matrix4 ): void;
 
 }

+ 14 - 8
src/objects/InstancedMesh.js

@@ -39,15 +39,9 @@ InstancedMesh.prototype = Object.assign( Object.create( Mesh.prototype ), {
 
 	},
 
-	setColorAt: function ( index, color ) {
-
-		if ( this.instanceColor === null ) {
-
-			this.instanceColor = new BufferAttribute( new Float32Array( this.count * 3 ), 3 );
+	getColorAt: function ( index, color ) {
 
-		}
-
-		color.toArray( this.instanceColor.array, index * 3 );
+		color.fromArray( this.instanceColor.array, index * 3 );
 
 	},
 
@@ -98,6 +92,18 @@ InstancedMesh.prototype = Object.assign( Object.create( Mesh.prototype ), {
 
 	},
 
+	setColorAt: function ( index, color ) {
+
+		if ( this.instanceColor === null ) {
+
+			this.instanceColor = new BufferAttribute( new Float32Array( this.count * 3 ), 3 );
+
+		}
+
+		color.toArray( this.instanceColor.array, index * 3 );
+
+	},
+
 	setMatrixAt: function ( index, matrix ) {
 
 		matrix.toArray( this.instanceMatrix.array, index * 16 );

+ 9 - 1
src/renderers/webgl/WebGLBindingStates.js

@@ -169,7 +169,7 @@
 		const cachedAttributes = currentState.attributes;
 		const geometryAttributes = geometry.attributes;
 
-		if ( Object.keys( cachedAttributes ).length !== Object.keys( geometryAttributes ).length ) return true;
+		let attributesNum = 0;
 
 		for ( const key in geometryAttributes ) {
 
@@ -182,8 +182,12 @@
 
 			if ( cachedAttribute.data !== geometryAttribute.data ) return true;
 
+			attributesNum ++;
+
 		}
 
+		if ( currentState.attributesNum !== attributesNum ) return true;
+
 		if ( currentState.index !== index ) return true;
 
 		return false;
@@ -194,6 +198,7 @@
 
 		const cache = {};
 		const attributes = geometry.attributes;
+		let attributesNum = 0;
 
 		for ( const key in attributes ) {
 
@@ -210,9 +215,12 @@
 
 			cache[ key ] = data;
 
+			attributesNum ++;
+
 		}
 
 		currentState.attributes = cache;
+		currentState.attributesNum = attributesNum;
 
 		currentState.index = index;
 

+ 24 - 3
test/unit/src/core/Object3D.tests.js

@@ -500,9 +500,30 @@ export default QUnit.module( 'Core', () => {
 
 		} );
 
-		QUnit.todo( "updateMatrix", ( assert ) => {
-
-			assert.ok( false, "everything's gonna be alright" );
+		QUnit.test( "updateMatrix", ( assert ) => {
+
+			const a = new Object3D();
+			a.position.set( 2, 3, 4 );
+			a.quaternion.set( 5, 6, 7, 8 );
+			a.scale.set( 9, 10, 11 );
+
+			assert.deepEqual( a.matrix.elements, [
+				1, 0, 0, 0,
+				0, 1, 0, 0,
+				0, 0, 1, 0,
+				0, 0, 0, 1
+			], "Updating position, quaternion, or scale has no effect to matrix until calling updateMatrix()" );
+
+			a.updateMatrix();
+
+			assert.deepEqual( a.matrix.elements, [
+				-1521, 1548, -234, 0,
+				-520, -1470, 1640, 0,
+				1826, 44, -1331, 0,
+				2, 3, 4, 1
+			], "matrix is calculated from position, quaternion, and scale" );
+
+			assert.equal( a.matrixWorldNeedsUpdate, true, "The flag indicating world matrix needs to be updated should be true" );
 
 		} );
 

+ 1 - 0
utils/modularize.js

@@ -117,6 +117,7 @@ var files = [
 	{ path: 'misc/Volume.js', dependencies: [ { name: 'VolumeSlice', path: 'misc/VolumeSlice.js' } ], ignoreList: [] },
 	{ path: 'misc/VolumeSlice.js', dependencies: [], ignoreList: [] },
 
+	{ path: 'modifiers/EdgeSplitModifier.js', dependencies: [ { name: 'BufferGeometryUtils', path: 'utils/BufferGeometryUtils.js' } ], ignoreList: [] },
 	{ path: 'modifiers/SimplifyModifier.js', dependencies: [], ignoreList: [] },
 	{ path: 'modifiers/SubdivisionModifier.js', dependencies: [], ignoreList: [] },
 	{ path: 'modifiers/TessellateModifier.js', dependencies: [], ignoreList: [] },

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