Selaa lähdekoodia

Examples: Convert modifiers to ES6. (#21604)

Michael Herzog 4 vuotta sitten
vanhempi
commit
85a366ce25

+ 94 - 85
examples/js/modifiers/EdgeSplitModifier.js

@@ -1,137 +1,144 @@
 ( function () {
 
-	var EdgeSplitModifier = function () {
+	const _A = new THREE.Vector3();
 
-		var A = new THREE.Vector3();
-		var B = new THREE.Vector3();
-		var C = new THREE.Vector3();
-		var positions, normals;
-		var indexes;
-		var pointToIndexMap, splitIndexes;
-		let oldNormals;
+	const _B = new THREE.Vector3();
 
-		function computeNormals() {
+	const _C = new THREE.Vector3();
 
-			normals = new Float32Array( indexes.length * 3 );
+	class EdgeSplitModifier {
 
-			for ( var i = 0; i < indexes.length; i += 3 ) {
+		modify( geometry, cutOffAngle, tryKeepNormals = true ) {
 
-				var index = indexes[ i ];
-				A.set( positions[ 3 * index ], positions[ 3 * index + 1 ], positions[ 3 * index + 2 ] );
-				index = indexes[ i + 1 ];
-				B.set( positions[ 3 * index ], positions[ 3 * index + 1 ], positions[ 3 * index + 2 ] );
-				index = indexes[ i + 2 ];
-				C.set( positions[ 3 * index ], positions[ 3 * index + 1 ], positions[ 3 * index + 2 ] );
-				C.sub( B );
-				A.sub( B );
-				var normal = C.cross( A ).normalize();
+			function computeNormals() {
 
-				for ( var j = 0; j < 3; j ++ ) {
+				normals = new Float32Array( indexes.length * 3 );
 
-					normals[ 3 * ( i + j ) ] = normal.x;
-					normals[ 3 * ( i + j ) + 1 ] = normal.y;
-					normals[ 3 * ( i + j ) + 2 ] = normal.z;
+				for ( let i = 0; i < indexes.length; i += 3 ) {
+
+					let index = indexes[ i ];
+
+					_A.set( positions[ 3 * index ], positions[ 3 * index + 1 ], positions[ 3 * index + 2 ] );
+
+					index = indexes[ i + 1 ];
+
+					_B.set( positions[ 3 * index ], positions[ 3 * index + 1 ], positions[ 3 * index + 2 ] );
+
+					index = indexes[ i + 2 ];
+
+					_C.set( positions[ 3 * index ], positions[ 3 * index + 1 ], positions[ 3 * index + 2 ] );
+
+					_C.sub( _B );
+
+					_A.sub( _B );
+
+					const normal = _C.cross( _A ).normalize();
+
+					for ( let j = 0; j < 3; j ++ ) {
+
+						normals[ 3 * ( i + j ) ] = normal.x;
+						normals[ 3 * ( i + j ) + 1 ] = normal.y;
+						normals[ 3 * ( i + j ) + 2 ] = normal.z;
+
+					}
 
 				}
 
 			}
 
-		}
+			function mapPositionsToIndexes() {
+
+				pointToIndexMap = Array( positions.length / 3 );
 
-		function mapPositionsToIndexes() {
+				for ( let i = 0; i < indexes.length; i ++ ) {
 
-			pointToIndexMap = Array( positions.length / 3 );
+					const index = indexes[ i ];
 
-			for ( var i = 0; i < indexes.length; i ++ ) {
+					if ( pointToIndexMap[ index ] == null ) {
 
-				var index = indexes[ i ];
+						pointToIndexMap[ index ] = [];
 
-				if ( pointToIndexMap[ index ] == null ) {
+					}
 
-					pointToIndexMap[ index ] = [];
+					pointToIndexMap[ index ].push( i );
 
 				}
 
-				pointToIndexMap[ index ].push( i );
-
 			}
 
-		}
+			function edgeSplitToGroups( indexes, cutOff, firstIndex ) {
+
+				_A.set( normals[ 3 * firstIndex ], normals[ 3 * firstIndex + 1 ], normals[ 3 * firstIndex + 2 ] ).normalize();
 
-		function edgeSplitToGroups( indexes, cutOff, firstIndex ) {
+				const result = {
+					splitGroup: [],
+					currentGroup: [ firstIndex ]
+				};
 
-			A.set( normals[ 3 * firstIndex ], normals[ 3 * firstIndex + 1 ], normals[ 3 * firstIndex + 2 ] ).normalize();
-			var result = {
-				splitGroup: [],
-				currentGroup: [ firstIndex ]
-			};
+				for ( const j of indexes ) {
 
-			for ( var j of indexes ) {
+					if ( j !== firstIndex ) {
 
-				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 );
 
-						result.splitGroup.push( j );
+						} else {
 
-					} else {
+							result.currentGroup.push( j );
 
-						result.currentGroup.push( j );
+						}
 
 					}
 
 				}
 
+				return result;
+
 			}
 
-			return result;
+			function edgeSplit( indexes, cutOff, original = null ) {
 
-		}
+				if ( indexes.length === 0 ) return;
+				const groupResults = [];
 
-		function edgeSplit( indexes, cutOff, original = null ) {
+				for ( const index of indexes ) {
 
-			if ( indexes.length === 0 ) return;
-			var groupResults = [];
+					groupResults.push( edgeSplitToGroups( indexes, cutOff, index ) );
 
-			for ( var index of indexes ) {
-
-				groupResults.push( edgeSplitToGroups( indexes, cutOff, index ) );
+				}
 
-			}
+				let result = groupResults[ 0 ];
 
-			var result = groupResults[ 0 ];
+				for ( const groupResult of groupResults ) {
 
-			for ( var groupResult of groupResults ) {
+					if ( groupResult.currentGroup.length > result.currentGroup.length ) {
 
-				if ( groupResult.currentGroup.length > result.currentGroup.length ) {
+						result = groupResult;
 
-					result = groupResult;
+					}
 
 				}
 
-			}
+				if ( original != null ) {
 
-			if ( original != null ) {
+					splitIndexes.push( {
+						original: original,
+						indexes: result.currentGroup
+					} );
 
-				splitIndexes.push( {
-					original: original,
-					indexes: result.currentGroup
-				} );
+				}
 
-			}
+				if ( result.splitGroup.length ) {
 
-			if ( result.splitGroup.length ) {
+					edgeSplit( result.splitGroup, cutOff, original || result.currentGroup[ 0 ] );
 
-				edgeSplit( result.splitGroup, cutOff, original || result.currentGroup[ 0 ] );
+				}
 
 			}
 
-		}
-
-		this.modify = function ( geometry, cutOffAngle, tryKeepNormals = true ) {
-
 			if ( geometry.isGeometry === true ) {
 
 				console.error( 'THREE.EdgeSplitModifier no longer supports THREE.Geometry. Use THREE.BufferGeometry instead.' );
@@ -140,7 +147,7 @@
 			}
 
 			let hadNormals = false;
-			oldNormals = null;
+			let oldNormals = null;
 
 			if ( geometry.attributes.normal ) {
 
@@ -169,13 +176,15 @@
 
 			}
 
-			indexes = geometry.index.array;
-			positions = geometry.getAttribute( 'position' ).array;
+			const indexes = geometry.index.array;
+			const positions = geometry.getAttribute( 'position' ).array;
+			let normals;
+			let pointToIndexMap;
 			computeNormals();
 			mapPositionsToIndexes();
-			splitIndexes = [];
+			const splitIndexes = [];
 
-			for ( var vertexIndexes of pointToIndexMap ) {
+			for ( const vertexIndexes of pointToIndexMap ) {
 
 				edgeSplit( vertexIndexes, Math.cos( cutOffAngle ) - 0.001 );
 
@@ -192,13 +201,13 @@
 
 			}
 
-			var newIndexes = new Uint32Array( indexes.length );
+			const newIndexes = new Uint32Array( indexes.length );
 			newIndexes.set( indexes );
 
-			for ( var i = 0; i < splitIndexes.length; i ++ ) {
+			for ( let i = 0; i < splitIndexes.length; i ++ ) {
 
-				var split = splitIndexes[ i ];
-				var index = indexes[ split.original ];
+				const split = splitIndexes[ i ];
+				const index = indexes[ split.original ];
 
 				for ( const attribute of Object.values( newAttributes ) ) {
 
@@ -210,7 +219,7 @@
 
 				}
 
-				for ( var j of split.indexes ) {
+				for ( const j of split.indexes ) {
 
 					newIndexes[ j ] = indexes.length + i;
 
@@ -253,9 +262,9 @@
 
 			return geometry;
 
-		};
+		}
 
-	};
+	}
 
 	THREE.EdgeSplitModifier = EdgeSplitModifier;
 

+ 282 - 276
examples/js/modifiers/SimplifyModifier.js

@@ -8,253 +8,370 @@
  *		- http://www.melax.com/polychop/
  */
 
-	var SimplifyModifier = function () {
+	const _cb = new THREE.Vector3(),
+		_ab = new THREE.Vector3();
 
-		if ( THREE.BufferGeometryUtils === undefined ) {
+	class SimplifyModifier {
 
-			throw 'THREE.SimplifyModifier relies on THREE.BufferGeometryUtils';
+		constructor() {
+
+			if ( THREE.BufferGeometryUtils === undefined ) {
+
+				throw 'THREE.SimplifyModifier relies on THREE.BufferGeometryUtils';
+
+			}
 
 		}
 
-	};
+		modify( geometry, count ) {
 
-	( function () {
+			if ( geometry.isGeometry === true ) {
 
-		var cb = new THREE.Vector3(),
-			ab = new THREE.Vector3();
+				console.error( 'THREE.SimplifyModifier no longer supports Geometry. Use THREE.BufferGeometry instead.' );
+				return;
 
-		function pushIfUnique( array, object ) {
+			}
 
-			if ( array.indexOf( object ) === - 1 ) array.push( object );
+			geometry = geometry.clone();
+			const attributes = geometry.attributes; // this modifier can only process indexed and non-indexed geomtries with a position attribute
 
-		}
+			for ( const name in attributes ) {
 
-		function removeFromArray( array, object ) {
+				if ( name !== 'position' ) geometry.deleteAttribute( name );
 
-			var k = array.indexOf( object );
-			if ( k > - 1 ) array.splice( k, 1 );
+			}
 
-		}
+			geometry = THREE.BufferGeometryUtils.mergeVertices( geometry ); //
+			// put data of original geometry in different data structures
+			//
 
-		function computeEdgeCollapseCost( u, v ) {
+			const vertices = [];
+			const faces = []; // add vertices
 
-			// if we collapse edge uv by moving u to v then how
-			// much different will the model change, i.e. the "error".
-			var edgelength = v.position.distanceTo( u.position );
-			var curvature = 0;
-			var sideFaces = [];
-			var i,
-				il = u.faces.length,
-				face,
-				sideFace; // find the "sides" triangles that are on the edge uv
+			const positionAttribute = geometry.getAttribute( 'position' );
 
-			for ( i = 0; i < il; i ++ ) {
+			for ( let i = 0; i < positionAttribute.count; i ++ ) {
 
-				face = u.faces[ i ];
+				const v = new THREE.Vector3().fromBufferAttribute( positionAttribute, i );
+				const vertex = new Vertex( v, i );
+				vertices.push( vertex );
+
+			} // add faces
 
-				if ( face.hasVertex( v ) ) {
 
-					sideFaces.push( face );
+			let index = geometry.getIndex();
+
+			if ( index !== null ) {
+
+				for ( let i = 0; i < index.count; i += 3 ) {
+
+					const a = index.getX( i );
+					const b = index.getX( i + 1 );
+					const c = index.getX( i + 2 );
+					const triangle = new Triangle( vertices[ a ], vertices[ b ], vertices[ c ], a, b, c );
+					faces.push( triangle );
 
 				}
 
-			} // use the triangle facing most away from the sides
-			// to determine our curvature term
+			} else {
 
+				for ( let i = 0; i < positionAttribute.count; i += 3 ) {
 
-			for ( i = 0; i < il; i ++ ) {
+					const a = i;
+					const b = i + 1;
+					const c = i + 2;
+					const triangle = new Triangle( vertices[ a ], vertices[ b ], vertices[ c ], a, b, c );
+					faces.push( triangle );
+
+				}
+
+			} // compute all edge collapse costs
 
-				var minCurvature = 1;
-				face = u.faces[ i ];
 
-				for ( var j = 0; j < sideFaces.length; j ++ ) {
+			for ( let i = 0, il = vertices.length; i < il; i ++ ) {
 
-					sideFace = sideFaces[ j ]; // use dot product of face normals.
+				computeEdgeCostAtVertex( vertices[ i ] );
+
+			}
 
-					var dotProd = face.normal.dot( sideFace.normal );
-					minCurvature = Math.min( minCurvature, ( 1.001 - dotProd ) / 2 );
+			let nextVertex;
+			let z = count;
+
+			while ( z -- ) {
+
+				nextVertex = minimumCostEdge( vertices );
+
+				if ( ! nextVertex ) {
+
+					console.log( 'THREE.SimplifyModifier: No next vertex' );
+					break;
 
 				}
 
-				curvature = Math.max( curvature, minCurvature );
+				collapse( vertices, faces, nextVertex, nextVertex.collapseNeighbor );
+
+			} //
+
 
-			} // crude approach in attempt to preserve borders
-			// though it seems not to be totally correct
+			const simplifiedGeometry = new THREE.BufferGeometry();
+			const position = [];
+			index = []; //
 
+			for ( let i = 0; i < vertices.length; i ++ ) {
 
-			var borders = 0;
+				const vertex = vertices[ i ].position;
+				position.push( vertex.x, vertex.y, vertex.z );
 
-			if ( sideFaces.length < 2 ) {
+			} //
 
-				// we add some arbitrary cost for borders,
-				// borders += 10;
-				curvature = 1;
 
-			}
+			for ( let i = 0; i < faces.length; i ++ ) {
 
-			var amt = edgelength * curvature + borders;
-			return amt;
+				const face = faces[ i ];
+				const a = vertices.indexOf( face.v1 );
+				const b = vertices.indexOf( face.v2 );
+				const c = vertices.indexOf( face.v3 );
+				index.push( a, b, c );
+
+			} //
+
+
+			simplifiedGeometry.setAttribute( 'position', new THREE.Float32BufferAttribute( position, 3 ) );
+			simplifiedGeometry.setIndex( index );
+			return simplifiedGeometry;
 
 		}
 
-		function computeEdgeCostAtVertex( v ) {
+	}
 
-			// compute the edge collapse cost for all edges that start
-			// from vertex v.	Since we are only interested in reducing
-			// the object by selecting the min cost edge at each step, we
-			// only cache the cost of the least cost edge at this vertex
-			// (in member variable collapse) as well as the value of the
-			// cost (in member variable collapseCost).
-			if ( v.neighbors.length === 0 ) {
+	function pushIfUnique( array, object ) {
 
-				// collapse if no neighbors.
-				v.collapseNeighbor = null;
-				v.collapseCost = - 0.01;
-				return;
+		if ( array.indexOf( object ) === - 1 ) array.push( object );
+
+	}
+
+	function removeFromArray( array, object ) {
+
+		var k = array.indexOf( object );
+		if ( k > - 1 ) array.splice( k, 1 );
+
+	}
+
+	function computeEdgeCollapseCost( u, v ) {
+
+		// if we collapse edge uv by moving u to v then how
+		// much different will the model change, i.e. the "error".
+		const edgelength = v.position.distanceTo( u.position );
+		let curvature = 0;
+		const sideFaces = []; // find the "sides" triangles that are on the edge uv
+
+		for ( let i = 0, il = u.faces.length; i < il; i ++ ) {
+
+			const face = u.faces[ i ];
+
+			if ( face.hasVertex( v ) ) {
+
+				sideFaces.push( face );
 
 			}
 
-			v.collapseCost = 100000;
-			v.collapseNeighbor = null; // search all neighboring edges for "least cost" edge
+		} // use the triangle facing most away from the sides
+		// to determine our curvature term
 
-			for ( var i = 0; i < v.neighbors.length; i ++ ) {
 
-				var collapseCost = computeEdgeCollapseCost( v, v.neighbors[ i ] );
+		for ( let i = 0, il = u.faces.length; i < il; i ++ ) {
 
-				if ( ! v.collapseNeighbor ) {
+			let minCurvature = 1;
+			const face = u.faces[ i ];
 
-					v.collapseNeighbor = v.neighbors[ i ];
-					v.collapseCost = collapseCost;
-					v.minCost = collapseCost;
-					v.totalCost = 0;
-					v.costCount = 0;
+			for ( let j = 0; j < sideFaces.length; j ++ ) {
 
-				}
+				const sideFace = sideFaces[ j ]; // use dot product of face normals.
+
+				const dotProd = face.normal.dot( sideFace.normal );
+				minCurvature = Math.min( minCurvature, ( 1.001 - dotProd ) / 2 );
 
-				v.costCount ++;
-				v.totalCost += collapseCost;
+			}
 
-				if ( collapseCost < v.minCost ) {
+			curvature = Math.max( curvature, minCurvature );
 
-					v.collapseNeighbor = v.neighbors[ i ];
-					v.minCost = collapseCost;
+		} // crude approach in attempt to preserve borders
+		// though it seems not to be totally correct
 
-				}
 
-			} // we average the cost of collapsing at this vertex
+		const borders = 0;
 
+		if ( sideFaces.length < 2 ) {
 
-			v.collapseCost = v.totalCost / v.costCount; // v.collapseCost = v.minCost;
+			// we add some arbitrary cost for borders,
+			// borders += 10;
+			curvature = 1;
 
 		}
 
-		function removeVertex( v, vertices ) {
-
-			console.assert( v.faces.length === 0 );
+		const amt = edgelength * curvature + borders;
+		return amt;
 
-			while ( v.neighbors.length ) {
+	}
 
-				var n = v.neighbors.pop();
-				removeFromArray( n.neighbors, v );
+	function computeEdgeCostAtVertex( v ) {
 
-			}
+		// compute the edge collapse cost for all edges that start
+		// from vertex v.	Since we are only interested in reducing
+		// the object by selecting the min cost edge at each step, we
+		// only cache the cost of the least cost edge at this vertex
+		// (in member variable collapse) as well as the value of the
+		// cost (in member variable collapseCost).
+		if ( v.neighbors.length === 0 ) {
 
-			removeFromArray( vertices, v );
+			// collapse if no neighbors.
+			v.collapseNeighbor = null;
+			v.collapseCost = - 0.01;
+			return;
 
 		}
 
-		function removeFace( f, faces ) {
+		v.collapseCost = 100000;
+		v.collapseNeighbor = null; // search all neighboring edges for "least cost" edge
+
+		for ( let i = 0; i < v.neighbors.length; i ++ ) {
+
+			const collapseCost = computeEdgeCollapseCost( v, v.neighbors[ i ] );
 
-			removeFromArray( faces, f );
-			if ( f.v1 ) removeFromArray( f.v1.faces, f );
-			if ( f.v2 ) removeFromArray( f.v2.faces, f );
-			if ( f.v3 ) removeFromArray( f.v3.faces, f ); // TODO optimize this!
+			if ( ! v.collapseNeighbor ) {
 
-			var vs = [ f.v1, f.v2, f.v3 ];
-			var v1, v2;
+				v.collapseNeighbor = v.neighbors[ i ];
+				v.collapseCost = collapseCost;
+				v.minCost = collapseCost;
+				v.totalCost = 0;
+				v.costCount = 0;
 
-			for ( var i = 0; i < 3; i ++ ) {
+			}
+
+			v.costCount ++;
+			v.totalCost += collapseCost;
 
-				v1 = vs[ i ];
-				v2 = vs[ ( i + 1 ) % 3 ];
-				if ( ! v1 || ! v2 ) continue;
-				v1.removeIfNonNeighbor( v2 );
-				v2.removeIfNonNeighbor( v1 );
+			if ( collapseCost < v.minCost ) {
+
+				v.collapseNeighbor = v.neighbors[ i ];
+				v.minCost = collapseCost;
 
 			}
 
+		} // we average the cost of collapsing at this vertex
+
+
+		v.collapseCost = v.totalCost / v.costCount; // v.collapseCost = v.minCost;
+
+	}
+
+	function removeVertex( v, vertices ) {
+
+		console.assert( v.faces.length === 0 );
+
+		while ( v.neighbors.length ) {
+
+			const n = v.neighbors.pop();
+			removeFromArray( n.neighbors, v );
+
 		}
 
-		function collapse( vertices, faces, u, v ) {
+		removeFromArray( vertices, v );
 
-			// u and v are pointers to vertices of an edge
-			// Collapse the edge uv by moving vertex u onto v
-			if ( ! v ) {
+	}
 
-				// u is a vertex all by itself so just delete it..
-				removeVertex( u, vertices );
-				return;
+	function removeFace( f, faces ) {
 
-			}
+		removeFromArray( faces, f );
+		if ( f.v1 ) removeFromArray( f.v1.faces, f );
+		if ( f.v2 ) removeFromArray( f.v2.faces, f );
+		if ( f.v3 ) removeFromArray( f.v3.faces, f ); // TODO optimize this!
 
-			var i;
-			var tmpVertices = [];
+		const vs = [ f.v1, f.v2, f.v3 ];
 
-			for ( i = 0; i < u.neighbors.length; i ++ ) {
+		for ( let i = 0; i < 3; i ++ ) {
 
-				tmpVertices.push( u.neighbors[ i ] );
+			const v1 = vs[ i ];
+			const v2 = vs[ ( i + 1 ) % 3 ];
+			if ( ! v1 || ! v2 ) continue;
+			v1.removeIfNonNeighbor( v2 );
+			v2.removeIfNonNeighbor( v1 );
 
-			} // delete triangles on edge uv:
+		}
 
+	}
 
-			for ( i = u.faces.length - 1; i >= 0; i -- ) {
+	function collapse( vertices, faces, u, v ) {
 
-				if ( u.faces[ i ].hasVertex( v ) ) {
+		// u and v are pointers to vertices of an edge
+		// Collapse the edge uv by moving vertex u onto v
+		if ( ! v ) {
 
-					removeFace( u.faces[ i ], faces );
+			// u is a vertex all by itself so just delete it..
+			removeVertex( u, vertices );
+			return;
 
-				}
+		}
 
-			} // update remaining triangles to have v instead of u
+		const tmpVertices = [];
 
+		for ( let i = 0; i < u.neighbors.length; i ++ ) {
 
-			for ( i = u.faces.length - 1; i >= 0; i -- ) {
+			tmpVertices.push( u.neighbors[ i ] );
 
-				u.faces[ i ].replaceVertex( u, v );
+		} // delete triangles on edge uv:
 
-			}
 
-			removeVertex( u, vertices ); // recompute the edge collapse costs in neighborhood
+		for ( let i = u.faces.length - 1; i >= 0; i -- ) {
 
-			for ( i = 0; i < tmpVertices.length; i ++ ) {
+			if ( u.faces[ i ].hasVertex( v ) ) {
 
-				computeEdgeCostAtVertex( tmpVertices[ i ] );
+				removeFace( u.faces[ i ], faces );
 
 			}
 
+		} // update remaining triangles to have v instead of u
+
+
+		for ( let i = u.faces.length - 1; i >= 0; i -- ) {
+
+			u.faces[ i ].replaceVertex( u, v );
+
 		}
 
-		function minimumCostEdge( vertices ) {
+		removeVertex( u, vertices ); // recompute the edge collapse costs in neighborhood
+
+		for ( let i = 0; i < tmpVertices.length; i ++ ) {
 
-			// O(n * n) approach. TODO optimize this
-			var least = vertices[ 0 ];
+			computeEdgeCostAtVertex( tmpVertices[ i ] );
 
-			for ( var i = 0; i < vertices.length; i ++ ) {
+		}
 
-				if ( vertices[ i ].collapseCost < least.collapseCost ) {
+	}
 
-					least = vertices[ i ];
+	function minimumCostEdge( vertices ) {
 
-				}
+		// O(n * n) approach. TODO optimize this
+		let least = vertices[ 0 ];
+
+		for ( let i = 0; i < vertices.length; i ++ ) {
+
+			if ( vertices[ i ].collapseCost < least.collapseCost ) {
+
+				least = vertices[ i ];
 
 			}
 
-			return least;
+		}
+
+		return least;
 
-		} // we use a triangle class to represent structure of face slightly differently
+	} // we use a triangle class to represent structure of face slightly differently
 
 
-		function Triangle( v1, v2, v3, a, b, c ) {
+	class Triangle {
+
+		constructor( v1, v2, v3, a, b, c ) {
 
 			this.a = a;
 			this.b = b;
@@ -276,25 +393,29 @@
 
 		}
 
-		Triangle.prototype.computeNormal = function () {
+		computeNormal() {
+
+			const vA = this.v1.position;
+			const vB = this.v2.position;
+			const vC = this.v3.position;
+
+			_cb.subVectors( vC, vB );
 
-			var vA = this.v1.position;
-			var vB = this.v2.position;
-			var vC = this.v3.position;
-			cb.subVectors( vC, vB );
-			ab.subVectors( vA, vB );
-			cb.cross( ab ).normalize();
-			this.normal.copy( cb );
+			_ab.subVectors( vA, vB );
 
-		};
+			_cb.cross( _ab ).normalize();
 
-		Triangle.prototype.hasVertex = function ( v ) {
+			this.normal.copy( _cb );
+
+		}
+
+		hasVertex( v ) {
 
 			return v === this.v1 || v === this.v2 || v === this.v3;
 
-		};
+		}
 
-		Triangle.prototype.replaceVertex = function ( oldv, newv ) {
+		replaceVertex( oldv, newv ) {
 
 			if ( oldv === this.v1 ) this.v1 = newv; else if ( oldv === this.v2 ) this.v2 = newv; else if ( oldv === this.v3 ) this.v3 = newv;
 			removeFromArray( oldv.faces, this );
@@ -313,9 +434,13 @@
 			this.v3.addUniqueNeighbor( this.v2 );
 			this.computeNormal();
 
-		};
+		}
+
+	}
 
-		function Vertex( v, id ) {
+	class Vertex {
+
+		constructor( v, id ) {
 
 			this.position = v;
 			this.id = id; // old index id
@@ -331,20 +456,20 @@
 
 		}
 
-		Vertex.prototype.addUniqueNeighbor = function ( vertex ) {
+		addUniqueNeighbor( vertex ) {
 
 			pushIfUnique( this.neighbors, vertex );
 
-		};
+		}
 
-		Vertex.prototype.removeIfNonNeighbor = function ( n ) {
+		removeIfNonNeighbor( n ) {
 
-			var neighbors = this.neighbors;
-			var faces = this.faces;
-			var offset = neighbors.indexOf( n );
+			const neighbors = this.neighbors;
+			const faces = this.faces;
+			const offset = neighbors.indexOf( n );
 			if ( offset === - 1 ) return;
 
-			for ( var i = 0; i < faces.length; i ++ ) {
+			for ( let i = 0; i < faces.length; i ++ ) {
 
 				if ( faces[ i ].hasVertex( n ) ) return;
 
@@ -352,128 +477,9 @@
 
 			neighbors.splice( offset, 1 );
 
-		};
-
-		SimplifyModifier.prototype.modify = function ( geometry, count ) {
-
-			if ( geometry.isGeometry === true ) {
-
-				console.error( 'THREE.SimplifyModifier no longer supports Geometry. Use THREE.BufferGeometry instead.' );
-				return;
-
-			}
-
-			geometry = geometry.clone();
-			var attributes = geometry.attributes; // this modifier can only process indexed and non-indexed geomtries with a position attribute
-
-			for ( var name in attributes ) {
-
-				if ( name !== 'position' ) geometry.deleteAttribute( name );
-
-			}
-
-			geometry = THREE.BufferGeometryUtils.mergeVertices( geometry ); //
-			// put data of original geometry in different data structures
-			//
-
-			var vertices = [];
-			var faces = []; // add vertices
-
-			var positionAttribute = geometry.getAttribute( 'position' );
-
-			for ( var i = 0; i < positionAttribute.count; i ++ ) {
-
-				var v = new THREE.Vector3().fromBufferAttribute( positionAttribute, i );
-				var vertex = new Vertex( v, i );
-				vertices.push( vertex );
-
-			} // add faces
-
-
-			var index = geometry.getIndex();
-
-			if ( index !== null ) {
-
-				for ( var i = 0; i < index.count; i += 3 ) {
-
-					var a = index.getX( i );
-					var b = index.getX( i + 1 );
-					var c = index.getX( i + 2 );
-					var triangle = new Triangle( vertices[ a ], vertices[ b ], vertices[ c ], a, b, c );
-					faces.push( triangle );
-
-				}
-
-			} else {
-
-				for ( var i = 0; i < positionAttribute.count; i += 3 ) {
-
-					var a = i;
-					var b = i + 1;
-					var c = i + 2;
-					var triangle = new Triangle( vertices[ a ], vertices[ b ], vertices[ c ], a, b, c );
-					faces.push( triangle );
-
-				}
-
-			} // compute all edge collapse costs
-
-
-			for ( var i = 0, il = vertices.length; i < il; i ++ ) {
-
-				computeEdgeCostAtVertex( vertices[ i ] );
-
-			}
-
-			var nextVertex;
-			var z = count;
-
-			while ( z -- ) {
-
-				nextVertex = minimumCostEdge( vertices );
-
-				if ( ! nextVertex ) {
-
-					console.log( 'THREE.SimplifyModifier: No next vertex' );
-					break;
-
-				}
-
-				collapse( vertices, faces, nextVertex, nextVertex.collapseNeighbor );
-
-			} //
-
-
-			var simplifiedGeometry = new THREE.BufferGeometry();
-			var position = [];
-			var index = []; //
-
-			for ( var i = 0; i < vertices.length; i ++ ) {
-
-				var vertex = vertices[ i ].position;
-				position.push( vertex.x, vertex.y, vertex.z );
-
-			} //
-
-
-			for ( var i = 0; i < faces.length; i ++ ) {
-
-				var face = faces[ i ];
-				var a = vertices.indexOf( face.v1 );
-				var b = vertices.indexOf( face.v2 );
-				var c = vertices.indexOf( face.v3 );
-				index.push( a, b, c );
-
-			} //
-
-
-			simplifiedGeometry.setAttribute( 'position', new THREE.Float32BufferAttribute( position, 3 ) );
-			simplifiedGeometry.setIndex( index );
-			return simplifiedGeometry;
-
-		};
+		}
 
-	} )();
+	}
 
 	THREE.SimplifyModifier = SimplifyModifier;
 

+ 199 - 195
examples/js/modifiers/TessellateModifier.js

@@ -4,282 +4,286 @@
  * Break faces with edges longer than maxEdgeLength
  */
 
-	var TessellateModifier = function ( maxEdgeLength = 0.1, maxIterations = 6 ) {
+	class TessellateModifier {
 
-		this.maxEdgeLength = maxEdgeLength;
-		this.maxIterations = maxIterations;
+		constructor( maxEdgeLength = 0.1, maxIterations = 6 ) {
 
-	};
+			this.maxEdgeLength = maxEdgeLength;
+			this.maxIterations = maxIterations;
 
-	TessellateModifier.prototype.modify = function ( geometry ) {
+		}
 
-		if ( geometry.isGeometry === true ) {
+		modify( geometry ) {
 
-			console.error( 'THREE.TessellateModifier no longer supports Geometry. Use THREE.BufferGeometry instead.' );
-			return geometry;
+			if ( geometry.isGeometry === true ) {
 
-		}
+				console.error( 'THREE.TessellateModifier no longer supports Geometry. Use THREE.BufferGeometry instead.' );
+				return geometry;
 
-		if ( geometry.index !== null ) {
-
-			geometry = geometry.toNonIndexed();
-
-		} //
-
-
-		const maxIterations = this.maxIterations;
-		const maxEdgeLengthSquared = this.maxEdgeLength * this.maxEdgeLength;
-		const va = new THREE.Vector3();
-		const vb = new THREE.Vector3();
-		const vc = new THREE.Vector3();
-		const vm = new THREE.Vector3();
-		const vs = [ va, vb, vc, vm ];
-		const na = new THREE.Vector3();
-		const nb = new THREE.Vector3();
-		const nc = new THREE.Vector3();
-		const nm = new THREE.Vector3();
-		const ns = [ na, nb, nc, nm ];
-		const ca = new THREE.Color();
-		const cb = new THREE.Color();
-		const cc = new THREE.Color();
-		const cm = new THREE.Color();
-		const cs = [ ca, cb, cc, cm ];
-		const ua = new THREE.Vector2();
-		const ub = new THREE.Vector2();
-		const uc = new THREE.Vector2();
-		const um = new THREE.Vector2();
-		const us = [ ua, ub, uc, um ];
-		const u2a = new THREE.Vector2();
-		const u2b = new THREE.Vector2();
-		const u2c = new THREE.Vector2();
-		const u2m = new THREE.Vector2();
-		const u2s = [ u2a, u2b, u2c, u2m ];
-		const attributes = geometry.attributes;
-		const hasNormals = attributes.normal !== undefined;
-		const hasColors = attributes.color !== undefined;
-		const hasUVs = attributes.uv !== undefined;
-		const hasUV2s = attributes.uv2 !== undefined;
-		let positions = attributes.position.array;
-		let normals = hasNormals ? attributes.normal.array : null;
-		let colors = hasColors ? attributes.color.array : null;
-		let uvs = hasUVs ? attributes.uv.array : null;
-		let uv2s = hasUV2s ? attributes.uv2.array : null;
-		let positions2 = positions;
-		let normals2 = normals;
-		let colors2 = colors;
-		let uvs2 = uvs;
-		let uv2s2 = uv2s;
-		let iteration = 0;
-		let tessellating = true;
-
-		function addTriangle( a, b, c ) {
-
-			const v1 = vs[ a ];
-			const v2 = vs[ b ];
-			const v3 = vs[ c ];
-			positions2.push( v1.x, v1.y, v1.z );
-			positions2.push( v2.x, v2.y, v2.z );
-			positions2.push( v3.x, v3.y, v3.z );
+			}
 
-			if ( hasNormals ) {
+			if ( geometry.index !== null ) {
+
+				geometry = geometry.toNonIndexed();
+
+			} //
+
+
+			const maxIterations = this.maxIterations;
+			const maxEdgeLengthSquared = this.maxEdgeLength * this.maxEdgeLength;
+			const va = new THREE.Vector3();
+			const vb = new THREE.Vector3();
+			const vc = new THREE.Vector3();
+			const vm = new THREE.Vector3();
+			const vs = [ va, vb, vc, vm ];
+			const na = new THREE.Vector3();
+			const nb = new THREE.Vector3();
+			const nc = new THREE.Vector3();
+			const nm = new THREE.Vector3();
+			const ns = [ na, nb, nc, nm ];
+			const ca = new THREE.Color();
+			const cb = new THREE.Color();
+			const cc = new THREE.Color();
+			const cm = new THREE.Color();
+			const cs = [ ca, cb, cc, cm ];
+			const ua = new THREE.Vector2();
+			const ub = new THREE.Vector2();
+			const uc = new THREE.Vector2();
+			const um = new THREE.Vector2();
+			const us = [ ua, ub, uc, um ];
+			const u2a = new THREE.Vector2();
+			const u2b = new THREE.Vector2();
+			const u2c = new THREE.Vector2();
+			const u2m = new THREE.Vector2();
+			const u2s = [ u2a, u2b, u2c, u2m ];
+			const attributes = geometry.attributes;
+			const hasNormals = attributes.normal !== undefined;
+			const hasColors = attributes.color !== undefined;
+			const hasUVs = attributes.uv !== undefined;
+			const hasUV2s = attributes.uv2 !== undefined;
+			let positions = attributes.position.array;
+			let normals = hasNormals ? attributes.normal.array : null;
+			let colors = hasColors ? attributes.color.array : null;
+			let uvs = hasUVs ? attributes.uv.array : null;
+			let uv2s = hasUV2s ? attributes.uv2.array : null;
+			let positions2 = positions;
+			let normals2 = normals;
+			let colors2 = colors;
+			let uvs2 = uvs;
+			let uv2s2 = uv2s;
+			let iteration = 0;
+			let tessellating = true;
+
+			function addTriangle( a, b, c ) {
+
+				const v1 = vs[ a ];
+				const v2 = vs[ b ];
+				const v3 = vs[ c ];
+				positions2.push( v1.x, v1.y, v1.z );
+				positions2.push( v2.x, v2.y, v2.z );
+				positions2.push( v3.x, v3.y, v3.z );
 
-				const n1 = ns[ a ];
-				const n2 = ns[ b ];
-				const n3 = ns[ c ];
-				normals2.push( n1.x, n1.y, n1.z );
-				normals2.push( n2.x, n2.y, n2.z );
-				normals2.push( n3.x, n3.y, n3.z );
+				if ( hasNormals ) {
 
-			}
+					const n1 = ns[ a ];
+					const n2 = ns[ b ];
+					const n3 = ns[ c ];
+					normals2.push( n1.x, n1.y, n1.z );
+					normals2.push( n2.x, n2.y, n2.z );
+					normals2.push( n3.x, n3.y, n3.z );
 
-			if ( hasColors ) {
+				}
 
-				const c1 = cs[ a ];
-				const c2 = cs[ b ];
-				const c3 = cs[ c ];
-				colors2.push( c1.x, c1.y, c1.z );
-				colors2.push( c2.x, c2.y, c2.z );
-				colors2.push( c3.x, c3.y, c3.z );
+				if ( hasColors ) {
 
-			}
+					const c1 = cs[ a ];
+					const c2 = cs[ b ];
+					const c3 = cs[ c ];
+					colors2.push( c1.x, c1.y, c1.z );
+					colors2.push( c2.x, c2.y, c2.z );
+					colors2.push( c3.x, c3.y, c3.z );
 
-			if ( hasUVs ) {
+				}
+
+				if ( hasUVs ) {
 
-				const u1 = us[ a ];
-				const u2 = us[ b ];
-				const u3 = us[ c ];
-				uvs2.push( u1.x, u1.y );
-				uvs2.push( u2.x, u2.y );
-				uvs2.push( u3.x, u3.y );
+					const u1 = us[ a ];
+					const u2 = us[ b ];
+					const u3 = us[ c ];
+					uvs2.push( u1.x, u1.y );
+					uvs2.push( u2.x, u2.y );
+					uvs2.push( u3.x, u3.y );
 
-			}
+				}
 
-			if ( hasUV2s ) {
+				if ( hasUV2s ) {
+
+					const u21 = u2s[ a ];
+					const u22 = u2s[ b ];
+					const u23 = u2s[ c ];
+					uv2s2.push( u21.x, u21.y );
+					uv2s2.push( u22.x, u22.y );
+					uv2s2.push( u23.x, u23.y );
 
-				const u21 = u2s[ a ];
-				const u22 = u2s[ b ];
-				const u23 = u2s[ c ];
-				uv2s2.push( u21.x, u21.y );
-				uv2s2.push( u22.x, u22.y );
-				uv2s2.push( u23.x, u23.y );
+				}
 
 			}
 
-		}
+			while ( tessellating && iteration < maxIterations ) {
 
-		while ( tessellating && iteration < maxIterations ) {
+				iteration ++;
+				tessellating = false;
+				positions = positions2;
+				positions2 = [];
 
-			iteration ++;
-			tessellating = false;
-			positions = positions2;
-			positions2 = [];
+				if ( hasNormals ) {
 
-			if ( hasNormals ) {
+					normals = normals2;
+					normals2 = [];
 
-				normals = normals2;
-				normals2 = [];
+				}
 
-			}
+				if ( hasColors ) {
 
-			if ( hasColors ) {
+					colors = colors2;
+					colors2 = [];
 
-				colors = colors2;
-				colors2 = [];
+				}
 
-			}
+				if ( hasUVs ) {
 
-			if ( hasUVs ) {
+					uvs = uvs2;
+					uvs2 = [];
 
-				uvs = uvs2;
-				uvs2 = [];
+				}
 
-			}
+				if ( hasUV2s ) {
 
-			if ( hasUV2s ) {
+					uv2s = uv2s2;
+					uv2s2 = [];
 
-				uv2s = uv2s2;
-				uv2s2 = [];
+				}
 
-			}
+				for ( let i = 0, i2 = 0, il = positions.length; i < il; i += 9, i2 += 6 ) {
 
-			for ( var i = 0, i2 = 0, il = positions.length; i < il; i += 9, i2 += 6 ) {
+					va.fromArray( positions, i + 0 );
+					vb.fromArray( positions, i + 3 );
+					vc.fromArray( positions, i + 6 );
 
-				va.fromArray( positions, i + 0 );
-				vb.fromArray( positions, i + 3 );
-				vc.fromArray( positions, i + 6 );
+					if ( hasNormals ) {
 
-				if ( hasNormals ) {
+						na.fromArray( normals, i + 0 );
+						nb.fromArray( normals, i + 3 );
+						nc.fromArray( normals, i + 6 );
 
-					na.fromArray( normals, i + 0 );
-					nb.fromArray( normals, i + 3 );
-					nc.fromArray( normals, i + 6 );
+					}
 
-				}
+					if ( hasColors ) {
 
-				if ( hasColors ) {
+						ca.fromArray( colors, i + 0 );
+						cb.fromArray( colors, i + 3 );
+						cc.fromArray( colors, i + 6 );
 
-					ca.fromArray( colors, i + 0 );
-					cb.fromArray( colors, i + 3 );
-					cc.fromArray( colors, i + 6 );
+					}
 
-				}
+					if ( hasUVs ) {
 
-				if ( hasUVs ) {
+						ua.fromArray( uvs, i2 + 0 );
+						ub.fromArray( uvs, i2 + 2 );
+						uc.fromArray( uvs, i2 + 4 );
 
-					ua.fromArray( uvs, i2 + 0 );
-					ub.fromArray( uvs, i2 + 2 );
-					uc.fromArray( uvs, i2 + 4 );
+					}
 
-				}
+					if ( hasUV2s ) {
 
-				if ( hasUV2s ) {
+						u2a.fromArray( uv2s, i2 + 0 );
+						u2b.fromArray( uv2s, i2 + 2 );
+						u2c.fromArray( uv2s, i2 + 4 );
 
-					u2a.fromArray( uv2s, i2 + 0 );
-					u2b.fromArray( uv2s, i2 + 2 );
-					u2c.fromArray( uv2s, i2 + 4 );
+					}
 
-				}
+					const dab = va.distanceToSquared( vb );
+					const dbc = vb.distanceToSquared( vc );
+					const dac = va.distanceToSquared( vc );
 
-				const dab = va.distanceToSquared( vb );
-				const dbc = vb.distanceToSquared( vc );
-				const dac = va.distanceToSquared( vc );
+					if ( dab > maxEdgeLengthSquared || dbc > maxEdgeLengthSquared || dac > maxEdgeLengthSquared ) {
 
-				if ( dab > maxEdgeLengthSquared || dbc > maxEdgeLengthSquared || dac > maxEdgeLengthSquared ) {
+						tessellating = true;
 
-					tessellating = true;
+						if ( dab >= dbc && dab >= dac ) {
 
-					if ( dab >= dbc && dab >= dac ) {
+							vm.lerpVectors( va, vb, 0.5 );
+							if ( hasNormals ) nm.lerpVectors( na, nb, 0.5 );
+							if ( hasColors ) cm.lerpColors( ca, cb, 0.5 );
+							if ( hasUVs ) um.lerpVectors( ua, ub, 0.5 );
+							if ( hasUV2s ) u2m.lerpVectors( u2a, u2b, 0.5 );
+							addTriangle( 0, 3, 2 );
+							addTriangle( 3, 1, 2 );
 
-						vm.lerpVectors( va, vb, 0.5 );
-						if ( hasNormals ) nm.lerpVectors( na, nb, 0.5 );
-						if ( hasColors ) cm.lerpColors( ca, cb, 0.5 );
-						if ( hasUVs ) um.lerpVectors( ua, ub, 0.5 );
-						if ( hasUV2s ) u2m.lerpVectors( u2a, u2b, 0.5 );
-						addTriangle( 0, 3, 2 );
-						addTriangle( 3, 1, 2 );
+						} else if ( dbc >= dab && dbc >= dac ) {
 
-					} else if ( dbc >= dab && dbc >= dac ) {
+							vm.lerpVectors( vb, vc, 0.5 );
+							if ( hasNormals ) nm.lerpVectors( nb, nc, 0.5 );
+							if ( hasColors ) cm.lerpColors( cb, cc, 0.5 );
+							if ( hasUVs ) um.lerpVectors( ub, uc, 0.5 );
+							if ( hasUV2s ) u2m.lerpVectors( u2b, u2c, 0.5 );
+							addTriangle( 0, 1, 3 );
+							addTriangle( 3, 2, 0 );
 
-						vm.lerpVectors( vb, vc, 0.5 );
-						if ( hasNormals ) nm.lerpVectors( nb, nc, 0.5 );
-						if ( hasColors ) cm.lerpColors( cb, cc, 0.5 );
-						if ( hasUVs ) um.lerpVectors( ub, uc, 0.5 );
-						if ( hasUV2s ) u2m.lerpVectors( u2b, u2c, 0.5 );
-						addTriangle( 0, 1, 3 );
-						addTriangle( 3, 2, 0 );
+						} else {
 
-					} else {
+							vm.lerpVectors( va, vc, 0.5 );
+							if ( hasNormals ) nm.lerpVectors( na, nc, 0.5 );
+							if ( hasColors ) cm.lerpColors( ca, cc, 0.5 );
+							if ( hasUVs ) um.lerpVectors( ua, uc, 0.5 );
+							if ( hasUV2s ) u2m.lerpVectors( u2a, u2c, 0.5 );
+							addTriangle( 0, 1, 3 );
+							addTriangle( 3, 1, 2 );
 
-						vm.lerpVectors( va, vc, 0.5 );
-						if ( hasNormals ) nm.lerpVectors( na, nc, 0.5 );
-						if ( hasColors ) cm.lerpColors( ca, cc, 0.5 );
-						if ( hasUVs ) um.lerpVectors( ua, uc, 0.5 );
-						if ( hasUV2s ) u2m.lerpVectors( u2a, u2c, 0.5 );
-						addTriangle( 0, 1, 3 );
-						addTriangle( 3, 1, 2 );
+						}
 
-					}
+					} else {
 
-				} else {
+						addTriangle( 0, 1, 2 );
 
-					addTriangle( 0, 1, 2 );
+					}
 
 				}
 
 			}
 
-		}
+			const geometry2 = new THREE.BufferGeometry();
+			geometry2.setAttribute( 'position', new THREE.Float32BufferAttribute( positions2, 3 ) );
+
+			if ( hasNormals ) {
 
-		const geometry2 = new THREE.BufferGeometry();
-		geometry2.setAttribute( 'position', new THREE.Float32BufferAttribute( positions2, 3 ) );
+				geometry2.setAttribute( 'normal', new THREE.Float32BufferAttribute( normals2, 3 ) );
 
-		if ( hasNormals ) {
+			}
 
-			geometry2.setAttribute( 'normal', new THREE.Float32BufferAttribute( normals2, 3 ) );
+			if ( hasColors ) {
 
-		}
+				geometry2.setAttribute( 'color', new THREE.Float32BufferAttribute( colors2, 3 ) );
 
-		if ( hasColors ) {
+			}
 
-			geometry2.setAttribute( 'color', new THREE.Float32BufferAttribute( colors2, 3 ) );
+			if ( hasUVs ) {
 
-		}
+				geometry2.setAttribute( 'uv', new THREE.Float32BufferAttribute( uvs2, 2 ) );
 
-		if ( hasUVs ) {
+			}
 
-			geometry2.setAttribute( 'uv', new THREE.Float32BufferAttribute( uvs2, 2 ) );
+			if ( hasUV2s ) {
 
-		}
+				geometry2.setAttribute( 'uv2', new THREE.Float32BufferAttribute( uv2s2, 2 ) );
 
-		if ( hasUV2s ) {
+			}
 
-			geometry2.setAttribute( 'uv2', new THREE.Float32BufferAttribute( uv2s2, 2 ) );
+			return geometry2;
 
 		}
 
-		return geometry2;
-
-	};
+	}
 
 	THREE.TessellateModifier = TessellateModifier;
 

+ 93 - 95
examples/jsm/modifiers/EdgeSplitModifier.js

@@ -5,161 +5,154 @@ import {
 } from '../../../build/three.module.js';
 import { BufferGeometryUtils } from '../utils/BufferGeometryUtils.js';
 
-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();
+class EdgeSplitModifier {
 
-	var positions, normals;
-	var indexes;
-	var pointToIndexMap, splitIndexes;
-	let oldNormals;
+	modify( geometry, cutOffAngle, tryKeepNormals = true ) {
 
+		function computeNormals() {
 
-	function computeNormals() {
+			normals = new Float32Array( indexes.length * 3 );
 
-		normals = new Float32Array( indexes.length * 3 );
+			for ( let i = 0; i < indexes.length; i += 3 ) {
 
-		for ( var i = 0; i < indexes.length; i += 3 ) {
+				let index = indexes[ i ];
 
-			var index = indexes[ i ];
+				_A.set(
+					positions[ 3 * index ],
+					positions[ 3 * index + 1 ],
+					positions[ 3 * index + 2 ] );
 
-			A.set(
-				positions[ 3 * index ],
-				positions[ 3 * index + 1 ],
-				positions[ 3 * index + 2 ] );
+				index = indexes[ i + 1 ];
+				_B.set(
+					positions[ 3 * index ],
+					positions[ 3 * index + 1 ],
+					positions[ 3 * index + 2 ] );
 
-			index = indexes[ i + 1 ];
-			B.set(
-				positions[ 3 * index ],
-				positions[ 3 * index + 1 ],
-				positions[ 3 * index + 2 ] );
+				index = indexes[ i + 2 ];
+				_C.set(
+					positions[ 3 * index ],
+					positions[ 3 * index + 1 ],
+					positions[ 3 * index + 2 ] );
 
-			index = indexes[ i + 2 ];
-			C.set(
-				positions[ 3 * index ],
-				positions[ 3 * index + 1 ],
-				positions[ 3 * index + 2 ] );
+				_C.sub( _B );
+				_A.sub( _B );
 
-			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;
+					normals[ 3 * ( i + j ) + 2 ] = normal.z;
 
-				normals[ 3 * ( i + j ) ] = normal.x;
-				normals[ 3 * ( i + j ) + 1 ] = normal.y;
-				normals[ 3 * ( i + j ) + 2 ] = normal.z;
+				}
 
 			}
 
 		}
 
-	}
 
+		function mapPositionsToIndexes() {
 
-	function mapPositionsToIndexes() {
+			pointToIndexMap = Array( positions.length / 3 );
 
-		pointToIndexMap = Array( positions.length / 3 );
+			for ( let i = 0; i < indexes.length; i ++ ) {
 
-		for ( var i = 0; i < indexes.length; i ++ ) {
+				const index = indexes[ i ];
 
-			var index = indexes[ i ];
+				if ( pointToIndexMap[ index ] == null ) {
 
-			if ( pointToIndexMap[ index ] == null ) {
+					pointToIndexMap[ index ] = [];
 
-				pointToIndexMap[ index ] = [];
+				}
 
-			}
+				pointToIndexMap[ index ].push( i );
 
-			pointToIndexMap[ index ].push( i );
+			}
 
 		}
 
-	}
 
+		function edgeSplitToGroups( indexes, cutOff, firstIndex ) {
 
-	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 = {
+				splitGroup: [],
+				currentGroup: [ firstIndex ]
+			};
 
-		var result = {
-			splitGroup: [],
-			currentGroup: [ firstIndex ]
-		};
+			for ( const j of indexes ) {
 
-		for ( var j of indexes ) {
+				if ( j !== firstIndex ) {
 
-			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 );
 
-					result.splitGroup.push( j );
+					} else {
 
-				} else {
+						result.currentGroup.push( j );
 
-					result.currentGroup.push( j );
+					}
 
 				}
 
 			}
 
-		}
+			return result;
 
-		return result;
+		}
 
-	}
 
+		function edgeSplit( indexes, cutOff, original = null ) {
 
-	function edgeSplit( indexes, cutOff, original = null ) {
+			if ( indexes.length === 0 ) return;
 
-		if ( indexes.length === 0 ) return;
+			const groupResults = [];
 
-		var groupResults = [];
+			for ( const index of indexes ) {
 
-		for ( var index of indexes ) {
+				groupResults.push( edgeSplitToGroups( indexes, cutOff, index ) );
 
-			groupResults.push( edgeSplitToGroups( indexes, cutOff, index ) );
+			}
 
-		}
+			let result = groupResults[ 0 ];
 
-		var result = groupResults[ 0 ];
+			for ( const groupResult of groupResults ) {
 
-		for ( var groupResult of groupResults ) {
+				if ( groupResult.currentGroup.length > result.currentGroup.length ) {
 
-			if ( groupResult.currentGroup.length > result.currentGroup.length ) {
+					result = groupResult;
 
-				result = groupResult;
+				}
 
 			}
 
-		}
 
+			if ( original != null ) {
 
-		if ( original != null ) {
+				splitIndexes.push( {
+					original: original,
+					indexes: result.currentGroup
+				} );
 
-			splitIndexes.push( {
-				original: original,
-				indexes: result.currentGroup
-			} );
+			}
 
-		}
+			if ( result.splitGroup.length ) {
 
-		if ( result.splitGroup.length ) {
+				edgeSplit( result.splitGroup, cutOff, original || result.currentGroup[ 0 ] );
 
-			edgeSplit( result.splitGroup, cutOff, original || result.currentGroup[ 0 ] );
+			}
 
 		}
 
-	}
-
-
-	this.modify = function ( geometry, cutOffAngle, tryKeepNormals = true ) {
-
 		if ( geometry.isGeometry === true ) {
 
 			console.error( 'THREE.EdgeSplitModifier no longer supports THREE.Geometry. Use BufferGeometry instead.' );
@@ -168,7 +161,7 @@ var EdgeSplitModifier = function () {
 		}
 
 		let hadNormals = false;
-		oldNormals = null;
+		let oldNormals = null;
 
 		if ( geometry.attributes.normal ) {
 
@@ -198,15 +191,18 @@ var EdgeSplitModifier = function () {
 
 		}
 
-		indexes = geometry.index.array;
-		positions = geometry.getAttribute( 'position' ).array;
+		const indexes = geometry.index.array;
+		const positions = geometry.getAttribute( 'position' ).array;
+
+		let normals;
+		let pointToIndexMap;
 
 		computeNormals();
 		mapPositionsToIndexes();
 
-		splitIndexes = [];
+		const splitIndexes = [];
 
-		for ( var vertexIndexes of pointToIndexMap ) {
+		for ( const vertexIndexes of pointToIndexMap ) {
 
 			edgeSplit( vertexIndexes, Math.cos( cutOffAngle ) - 0.001 );
 
@@ -222,13 +218,13 @@ var EdgeSplitModifier = function () {
 
 		}
 
-		var newIndexes = new Uint32Array( indexes.length );
+		const newIndexes = new Uint32Array( indexes.length );
 		newIndexes.set( indexes );
 
-		for ( var i = 0; i < splitIndexes.length; i ++ ) {
+		for ( let i = 0; i < splitIndexes.length; i ++ ) {
 
-			var split = splitIndexes[ i ];
-			var index = indexes[ split.original ];
+			const split = splitIndexes[ i ];
+			const index = indexes[ split.original ];
 
 			for ( const attribute of Object.values( newAttributes ) ) {
 
@@ -241,7 +237,7 @@ var EdgeSplitModifier = function () {
 
 			}
 
-			for ( var j of split.indexes ) {
+			for ( const j of split.indexes ) {
 
 				newIndexes[ j ] = indexes.length + i;
 
@@ -287,8 +283,10 @@ var EdgeSplitModifier = function () {
 
 		return geometry;
 
-	};
+	}
+
+}
+
 
-};
 
 export { EdgeSplitModifier };

+ 303 - 294
examples/jsm/modifiers/SimplifyModifier.js

@@ -13,524 +13,533 @@ import { BufferGeometryUtils } from '../utils/BufferGeometryUtils.js';
  *    - http://www.melax.com/polychop/
  */
 
-var SimplifyModifier = function () {
+const _cb = new Vector3(), _ab = new Vector3();
 
-	if ( BufferGeometryUtils === undefined ) {
+class SimplifyModifier {
 
-		throw 'THREE.SimplifyModifier relies on BufferGeometryUtils';
+	constructor() {
 
-	}
+		if ( BufferGeometryUtils === undefined ) {
 
-};
+			throw 'THREE.SimplifyModifier relies on BufferGeometryUtils';
 
-( function () {
+		}
 
-	var cb = new Vector3(), ab = new Vector3();
+	}
 
-	function pushIfUnique( array, object ) {
+	modify( geometry, count ) {
 
-		if ( array.indexOf( object ) === - 1 ) array.push( object );
+		if ( geometry.isGeometry === true ) {
 
-	}
+			console.error( 'THREE.SimplifyModifier no longer supports Geometry. Use BufferGeometry instead.' );
+			return;
 
-	function removeFromArray( array, object ) {
+		}
 
-		var k = array.indexOf( object );
-		if ( k > - 1 ) array.splice( k, 1 );
+		geometry = geometry.clone();
+		const attributes = geometry.attributes;
 
-	}
+		// this modifier can only process indexed and non-indexed geomtries with a position attribute
+
+		for ( const name in attributes ) {
 
-	function computeEdgeCollapseCost( u, v ) {
+			if ( name !== 'position' ) geometry.deleteAttribute( name );
+
+		}
 
-		// if we collapse edge uv by moving u to v then how
-		// much different will the model change, i.e. the "error".
+		geometry = BufferGeometryUtils.mergeVertices( geometry );
 
-		var edgelength = v.position.distanceTo( u.position );
-		var curvature = 0;
+		//
+		// put data of original geometry in different data structures
+		//
 
-		var sideFaces = [];
-		var i, il = u.faces.length, face, sideFace;
+		const vertices = [];
+		const faces = [];
 
-		// find the "sides" triangles that are on the edge uv
-		for ( i = 0; i < il; i ++ ) {
+		// add vertices
 
-			face = u.faces[ i ];
+		const positionAttribute = geometry.getAttribute( 'position' );
 
-			if ( face.hasVertex( v ) ) {
+		for ( let i = 0; i < positionAttribute.count; i ++ ) {
 
-				sideFaces.push( face );
+			const v = new Vector3().fromBufferAttribute( positionAttribute, i );
 
-			}
+			const vertex = new Vertex( v, i );
+			vertices.push( vertex );
 
 		}
 
-		// use the triangle facing most away from the sides
-		// to determine our curvature term
-		for ( i = 0; i < il; i ++ ) {
+		// add faces
 
-			var minCurvature = 1;
-			face = u.faces[ i ];
+		let index = geometry.getIndex();
 
-			for ( var j = 0; j < sideFaces.length; j ++ ) {
+		if ( index !== null ) {
+
+			for ( let i = 0; i < index.count; i += 3 ) {
 
-				sideFace = sideFaces[ j ];
-				// use dot product of face normals.
-				var dotProd = face.normal.dot( sideFace.normal );
-				minCurvature = Math.min( minCurvature, ( 1.001 - dotProd ) / 2 );
+				const a = index.getX( i );
+				const b = index.getX( i + 1 );
+				const c = index.getX( i + 2 );
+
+				const triangle = new Triangle( vertices[ a ], vertices[ b ], vertices[ c ], a, b, c );
+				faces.push( triangle );
 
 			}
 
-			curvature = Math.max( curvature, minCurvature );
+		} else {
 
-		}
+			for ( let i = 0; i < positionAttribute.count; i += 3 ) {
+
+				const a = i;
+				const b = i + 1;
+				const c = i + 2;
 
-		// crude approach in attempt to preserve borders
-		// though it seems not to be totally correct
-		var borders = 0;
-		if ( sideFaces.length < 2 ) {
+				const triangle = new Triangle( vertices[ a ], vertices[ b ], vertices[ c ], a, b, c );
+				faces.push( triangle );
 
-			// we add some arbitrary cost for borders,
-			// borders += 10;
-			curvature = 1;
+			}
 
 		}
 
-		var amt = edgelength * curvature + borders;
+		// compute all edge collapse costs
 
-		return amt;
+		for ( let i = 0, il = vertices.length; i < il; i ++ ) {
 
-	}
+			computeEdgeCostAtVertex( vertices[ i ] );
 
-	function computeEdgeCostAtVertex( v ) {
+		}
 
-		// compute the edge collapse cost for all edges that start
-		// from vertex v.  Since we are only interested in reducing
-		// the object by selecting the min cost edge at each step, we
-		// only cache the cost of the least cost edge at this vertex
-		// (in member variable collapse) as well as the value of the
-		// cost (in member variable collapseCost).
+		let nextVertex;
 
-		if ( v.neighbors.length === 0 ) {
+		let z = count;
 
-			// collapse if no neighbors.
-			v.collapseNeighbor = null;
-			v.collapseCost = - 0.01;
+		while ( z -- ) {
 
-			return;
+			nextVertex = minimumCostEdge( vertices );
 
-		}
+			if ( ! nextVertex ) {
 
-		v.collapseCost = 100000;
-		v.collapseNeighbor = null;
+				console.log( 'THREE.SimplifyModifier: No next vertex' );
+				break;
 
-		// search all neighboring edges for "least cost" edge
-		for ( var i = 0; i < v.neighbors.length; i ++ ) {
+			}
 
-			var collapseCost = computeEdgeCollapseCost( v, v.neighbors[ i ] );
+			collapse( vertices, faces, nextVertex, nextVertex.collapseNeighbor );
 
-			if ( ! v.collapseNeighbor ) {
+		}
 
-				v.collapseNeighbor = v.neighbors[ i ];
-				v.collapseCost = collapseCost;
-				v.minCost = collapseCost;
-				v.totalCost = 0;
-				v.costCount = 0;
+		//
 
-			}
+		const simplifiedGeometry = new BufferGeometry();
+		const position = [];
 
-			v.costCount ++;
-			v.totalCost += collapseCost;
+		index = [];
 
-			if ( collapseCost < v.minCost ) {
+		//
 
-				v.collapseNeighbor = v.neighbors[ i ];
-				v.minCost = collapseCost;
+		for ( let i = 0; i < vertices.length; i ++ ) {
 
-			}
+			const vertex = vertices[ i ].position;
+			position.push( vertex.x, vertex.y, vertex.z );
 
 		}
 
-		// we average the cost of collapsing at this vertex
-		v.collapseCost = v.totalCost / v.costCount;
-		// v.collapseCost = v.minCost;
-
-	}
+		//
 
-	function removeVertex( v, vertices ) {
+		for ( let i = 0; i < faces.length; i ++ ) {
 
-		console.assert( v.faces.length === 0 );
+			const face = faces[ i ];
 
-		while ( v.neighbors.length ) {
+			const a = vertices.indexOf( face.v1 );
+			const b = vertices.indexOf( face.v2 );
+			const c = vertices.indexOf( face.v3 );
 
-			var n = v.neighbors.pop();
-			removeFromArray( n.neighbors, v );
+			index.push( a, b, c );
 
 		}
 
-		removeFromArray( vertices, v );
+		//
+
+		simplifiedGeometry.setAttribute( 'position', new Float32BufferAttribute( position, 3 ) );
+		simplifiedGeometry.setIndex( index );
+
+		return simplifiedGeometry;
 
 	}
 
-	function removeFace( f, faces ) {
+}
 
-		removeFromArray( faces, f );
+function pushIfUnique( array, object ) {
 
-		if ( f.v1 ) removeFromArray( f.v1.faces, f );
-		if ( f.v2 ) removeFromArray( f.v2.faces, f );
-		if ( f.v3 ) removeFromArray( f.v3.faces, f );
+	if ( array.indexOf( object ) === - 1 ) array.push( object );
 
-		// TODO optimize this!
-		var vs = [ f.v1, f.v2, f.v3 ];
-		var v1, v2;
+}
 
-		for ( var i = 0; i < 3; i ++ ) {
+function removeFromArray( array, object ) {
 
-			v1 = vs[ i ];
-			v2 = vs[ ( i + 1 ) % 3 ];
+	var k = array.indexOf( object );
+	if ( k > - 1 ) array.splice( k, 1 );
 
-			if ( ! v1 || ! v2 ) continue;
+}
 
-			v1.removeIfNonNeighbor( v2 );
-			v2.removeIfNonNeighbor( v1 );
+function computeEdgeCollapseCost( u, v ) {
 
-		}
+	// if we collapse edge uv by moving u to v then how
+	// much different will the model change, i.e. the "error".
 
-	}
+	const edgelength = v.position.distanceTo( u.position );
+	let curvature = 0;
 
-	function collapse( vertices, faces, u, v ) { // u and v are pointers to vertices of an edge
+	const sideFaces = [];
 
-		// Collapse the edge uv by moving vertex u onto v
+	// find the "sides" triangles that are on the edge uv
+	for ( let i = 0, il = u.faces.length; i < il; i ++ ) {
 
-		if ( ! v ) {
+		const face = u.faces[ i ];
 
-			// u is a vertex all by itself so just delete it..
-			removeVertex( u, vertices );
-			return;
+		if ( face.hasVertex( v ) ) {
+
+			sideFaces.push( face );
 
 		}
 
-		var i;
-		var tmpVertices = [];
+	}
+
+	// use the triangle facing most away from the sides
+	// to determine our curvature term
+	for ( let i = 0, il = u.faces.length; i < il; i ++ ) {
 
-		for ( i = 0; i < u.neighbors.length; i ++ ) {
+		let minCurvature = 1;
+		const face = u.faces[ i ];
 
-			tmpVertices.push( u.neighbors[ i ] );
+		for ( let j = 0; j < sideFaces.length; j ++ ) {
+
+			const sideFace = sideFaces[ j ];
+			// use dot product of face normals.
+			const dotProd = face.normal.dot( sideFace.normal );
+			minCurvature = Math.min( minCurvature, ( 1.001 - dotProd ) / 2 );
 
 		}
 
+		curvature = Math.max( curvature, minCurvature );
 
-		// delete triangles on edge uv:
-		for ( i = u.faces.length - 1; i >= 0; i -- ) {
+	}
 
-			if ( u.faces[ i ].hasVertex( v ) ) {
+	// crude approach in attempt to preserve borders
+	// though it seems not to be totally correct
+	const borders = 0;
 
-				removeFace( u.faces[ i ], faces );
+	if ( sideFaces.length < 2 ) {
 
-			}
+		// we add some arbitrary cost for borders,
+		// borders += 10;
+		curvature = 1;
 
-		}
+	}
 
-		// update remaining triangles to have v instead of u
-		for ( i = u.faces.length - 1; i >= 0; i -- ) {
+	const amt = edgelength * curvature + borders;
 
-			u.faces[ i ].replaceVertex( u, v );
+	return amt;
 
-		}
+}
 
+function computeEdgeCostAtVertex( v ) {
 
-		removeVertex( u, vertices );
+	// compute the edge collapse cost for all edges that start
+	// from vertex v.  Since we are only interested in reducing
+	// the object by selecting the min cost edge at each step, we
+	// only cache the cost of the least cost edge at this vertex
+	// (in member variable collapse) as well as the value of the
+	// cost (in member variable collapseCost).
 
-		// recompute the edge collapse costs in neighborhood
-		for ( i = 0; i < tmpVertices.length; i ++ ) {
+	if ( v.neighbors.length === 0 ) {
 
-			computeEdgeCostAtVertex( tmpVertices[ i ] );
+		// collapse if no neighbors.
+		v.collapseNeighbor = null;
+		v.collapseCost = - 0.01;
 
-		}
+		return;
 
 	}
 
+	v.collapseCost = 100000;
+	v.collapseNeighbor = null;
 
+	// search all neighboring edges for "least cost" edge
+	for ( let i = 0; i < v.neighbors.length; i ++ ) {
 
-	function minimumCostEdge( vertices ) {
+		const collapseCost = computeEdgeCollapseCost( v, v.neighbors[ i ] );
 
-		// O(n * n) approach. TODO optimize this
+		if ( ! v.collapseNeighbor ) {
 
-		var least = vertices[ 0 ];
+			v.collapseNeighbor = v.neighbors[ i ];
+			v.collapseCost = collapseCost;
+			v.minCost = collapseCost;
+			v.totalCost = 0;
+			v.costCount = 0;
 
-		for ( var i = 0; i < vertices.length; i ++ ) {
+		}
 
-			if ( vertices[ i ].collapseCost < least.collapseCost ) {
+		v.costCount ++;
+		v.totalCost += collapseCost;
 
-				least = vertices[ i ];
+		if ( collapseCost < v.minCost ) {
 
-			}
+			v.collapseNeighbor = v.neighbors[ i ];
+			v.minCost = collapseCost;
 
 		}
 
-		return least;
-
 	}
 
-	// we use a triangle class to represent structure of face slightly differently
+	// we average the cost of collapsing at this vertex
+	v.collapseCost = v.totalCost / v.costCount;
+	// v.collapseCost = v.minCost;
 
-	function Triangle( v1, v2, v3, a, b, c ) {
+}
 
-		this.a = a;
-		this.b = b;
-		this.c = c;
+function removeVertex( v, vertices ) {
 
-		this.v1 = v1;
-		this.v2 = v2;
-		this.v3 = v3;
+	console.assert( v.faces.length === 0 );
 
-		this.normal = new Vector3();
+	while ( v.neighbors.length ) {
 
-		this.computeNormal();
+		const n = v.neighbors.pop();
+		removeFromArray( n.neighbors, v );
 
-		v1.faces.push( this );
-		v1.addUniqueNeighbor( v2 );
-		v1.addUniqueNeighbor( v3 );
+	}
 
-		v2.faces.push( this );
-		v2.addUniqueNeighbor( v1 );
-		v2.addUniqueNeighbor( v3 );
+	removeFromArray( vertices, v );
 
+}
 
-		v3.faces.push( this );
-		v3.addUniqueNeighbor( v1 );
-		v3.addUniqueNeighbor( v2 );
+function removeFace( f, faces ) {
 
-	}
+	removeFromArray( faces, f );
 
-	Triangle.prototype.computeNormal = function () {
+	if ( f.v1 ) removeFromArray( f.v1.faces, f );
+	if ( f.v2 ) removeFromArray( f.v2.faces, f );
+	if ( f.v3 ) removeFromArray( f.v3.faces, f );
 
-		var vA = this.v1.position;
-		var vB = this.v2.position;
-		var vC = this.v3.position;
+	// TODO optimize this!
+	const vs = [ f.v1, f.v2, f.v3 ];
 
-		cb.subVectors( vC, vB );
-		ab.subVectors( vA, vB );
-		cb.cross( ab ).normalize();
+	for ( let i = 0; i < 3; i ++ ) {
 
-		this.normal.copy( cb );
+		const v1 = vs[ i ];
+		const v2 = vs[ ( i + 1 ) % 3 ];
 
-	};
+		if ( ! v1 || ! v2 ) continue;
 
-	Triangle.prototype.hasVertex = function ( v ) {
+		v1.removeIfNonNeighbor( v2 );
+		v2.removeIfNonNeighbor( v1 );
 
-		return v === this.v1 || v === this.v2 || v === this.v3;
+	}
 
-	};
+}
 
-	Triangle.prototype.replaceVertex = function ( oldv, newv ) {
+function collapse( vertices, faces, u, v ) { // u and v are pointers to vertices of an edge
 
-		if ( oldv === this.v1 ) this.v1 = newv;
-		else if ( oldv === this.v2 ) this.v2 = newv;
-		else if ( oldv === this.v3 ) this.v3 = newv;
+	// Collapse the edge uv by moving vertex u onto v
 
-		removeFromArray( oldv.faces, this );
-		newv.faces.push( this );
+	if ( ! v ) {
 
+		// u is a vertex all by itself so just delete it..
+		removeVertex( u, vertices );
+		return;
 
-		oldv.removeIfNonNeighbor( this.v1 );
-		this.v1.removeIfNonNeighbor( oldv );
+	}
 
-		oldv.removeIfNonNeighbor( this.v2 );
-		this.v2.removeIfNonNeighbor( oldv );
+	const tmpVertices = [];
 
-		oldv.removeIfNonNeighbor( this.v3 );
-		this.v3.removeIfNonNeighbor( oldv );
+	for ( let i = 0; i < u.neighbors.length; i ++ ) {
 
-		this.v1.addUniqueNeighbor( this.v2 );
-		this.v1.addUniqueNeighbor( this.v3 );
+		tmpVertices.push( u.neighbors[ i ] );
 
-		this.v2.addUniqueNeighbor( this.v1 );
-		this.v2.addUniqueNeighbor( this.v3 );
+	}
 
-		this.v3.addUniqueNeighbor( this.v1 );
-		this.v3.addUniqueNeighbor( this.v2 );
 
-		this.computeNormal();
+	// delete triangles on edge uv:
+	for ( let i = u.faces.length - 1; i >= 0; i -- ) {
 
-	};
+		if ( u.faces[ i ].hasVertex( v ) ) {
 
-	function Vertex( v, id ) {
+			removeFace( u.faces[ i ], faces );
 
-		this.position = v;
+		}
 
-		this.id = id; // old index id
+	}
 
-		this.faces = []; // faces vertex is connected
-		this.neighbors = []; // neighbouring vertices aka "adjacentVertices"
+	// update remaining triangles to have v instead of u
+	for ( let i = u.faces.length - 1; i >= 0; i -- ) {
 
-		// these will be computed in computeEdgeCostAtVertex()
-		this.collapseCost = 0; // cost of collapsing this vertex, the less the better. aka objdist
-		this.collapseNeighbor = null; // best candinate for collapsing
+		u.faces[ i ].replaceVertex( u, v );
 
 	}
 
-	Vertex.prototype.addUniqueNeighbor = function ( vertex ) {
 
-		pushIfUnique( this.neighbors, vertex );
+	removeVertex( u, vertices );
 
-	};
+	// recompute the edge collapse costs in neighborhood
+	for ( let i = 0; i < tmpVertices.length; i ++ ) {
 
-	Vertex.prototype.removeIfNonNeighbor = function ( n ) {
+		computeEdgeCostAtVertex( tmpVertices[ i ] );
 
-		var neighbors = this.neighbors;
-		var faces = this.faces;
+	}
 
-		var offset = neighbors.indexOf( n );
-		if ( offset === - 1 ) return;
-		for ( var i = 0; i < faces.length; i ++ ) {
+}
 
-			if ( faces[ i ].hasVertex( n ) ) return;
 
-		}
 
-		neighbors.splice( offset, 1 );
+function minimumCostEdge( vertices ) {
 
-	};
+	// O(n * n) approach. TODO optimize this
 
-	SimplifyModifier.prototype.modify = function ( geometry, count ) {
+	let least = vertices[ 0 ];
 
-		if ( geometry.isGeometry === true ) {
+	for ( let i = 0; i < vertices.length; i ++ ) {
 
-			console.error( 'THREE.SimplifyModifier no longer supports Geometry. Use BufferGeometry instead.' );
-			return;
+		if ( vertices[ i ].collapseCost < least.collapseCost ) {
+
+			least = vertices[ i ];
 
 		}
 
-		geometry = geometry.clone();
-		var attributes = geometry.attributes;
+	}
 
-		// this modifier can only process indexed and non-indexed geomtries with a position attribute
+	return least;
 
-		for ( var name in attributes ) {
+}
 
-			if ( name !== 'position' ) geometry.deleteAttribute( name );
+// we use a triangle class to represent structure of face slightly differently
 
-		}
+class Triangle {
 
-		geometry = BufferGeometryUtils.mergeVertices( geometry );
+	constructor( v1, v2, v3, a, b, c ) {
 
-		//
-		// put data of original geometry in different data structures
-		//
+		this.a = a;
+		this.b = b;
+		this.c = c;
 
-		var vertices = [];
-		var faces = [];
+		this.v1 = v1;
+		this.v2 = v2;
+		this.v3 = v3;
 
-		// add vertices
+		this.normal = new Vector3();
 
-		var positionAttribute = geometry.getAttribute( 'position' );
+		this.computeNormal();
 
-		for ( var i = 0; i < positionAttribute.count; i ++ ) {
+		v1.faces.push( this );
+		v1.addUniqueNeighbor( v2 );
+		v1.addUniqueNeighbor( v3 );
 
-			var v = new Vector3().fromBufferAttribute( positionAttribute, i );
+		v2.faces.push( this );
+		v2.addUniqueNeighbor( v1 );
+		v2.addUniqueNeighbor( v3 );
 
-			var vertex = new Vertex( v, i );
-			vertices.push( vertex );
 
-		}
+		v3.faces.push( this );
+		v3.addUniqueNeighbor( v1 );
+		v3.addUniqueNeighbor( v2 );
 
-		// add faces
+	}
 
-		var index = geometry.getIndex();
+	computeNormal() {
 
-		if ( index !== null ) {
+		const vA = this.v1.position;
+		const vB = this.v2.position;
+		const vC = this.v3.position;
 
-			for ( var i = 0; i < index.count; i += 3 ) {
+		_cb.subVectors( vC, vB );
+		_ab.subVectors( vA, vB );
+		_cb.cross( _ab ).normalize();
 
-				var a = index.getX( i );
-				var b = index.getX( i + 1 );
-				var c = index.getX( i + 2 );
+		this.normal.copy( _cb );
 
-				var triangle = new Triangle( vertices[ a ], vertices[ b ], vertices[ c ], a, b, c );
-				faces.push( triangle );
+	}
 
-			}
+	hasVertex( v ) {
 
-		} else {
+		return v === this.v1 || v === this.v2 || v === this.v3;
 
-			for ( var i = 0; i < positionAttribute.count; i += 3 ) {
+	}
 
-				var a = i;
-				var b = i + 1;
-				var c = i + 2;
+	replaceVertex( oldv, newv ) {
 
-				var triangle = new Triangle( vertices[ a ], vertices[ b ], vertices[ c ], a, b, c );
-				faces.push( triangle );
+		if ( oldv === this.v1 ) this.v1 = newv;
+		else if ( oldv === this.v2 ) this.v2 = newv;
+		else if ( oldv === this.v3 ) this.v3 = newv;
 
-			}
+		removeFromArray( oldv.faces, this );
+		newv.faces.push( this );
 
-		}
 
-		// compute all edge collapse costs
+		oldv.removeIfNonNeighbor( this.v1 );
+		this.v1.removeIfNonNeighbor( oldv );
 
-		for ( var i = 0, il = vertices.length; i < il; i ++ ) {
+		oldv.removeIfNonNeighbor( this.v2 );
+		this.v2.removeIfNonNeighbor( oldv );
 
-			computeEdgeCostAtVertex( vertices[ i ] );
+		oldv.removeIfNonNeighbor( this.v3 );
+		this.v3.removeIfNonNeighbor( oldv );
 
-		}
+		this.v1.addUniqueNeighbor( this.v2 );
+		this.v1.addUniqueNeighbor( this.v3 );
 
-		var nextVertex;
+		this.v2.addUniqueNeighbor( this.v1 );
+		this.v2.addUniqueNeighbor( this.v3 );
 
-		var z = count;
+		this.v3.addUniqueNeighbor( this.v1 );
+		this.v3.addUniqueNeighbor( this.v2 );
 
-		while ( z -- ) {
+		this.computeNormal();
 
-			nextVertex = minimumCostEdge( vertices );
+	}
 
-			if ( ! nextVertex ) {
+}
 
-				console.log( 'THREE.SimplifyModifier: No next vertex' );
-				break;
+class Vertex {
 
-			}
+	constructor( v, id ) {
 
-			collapse( vertices, faces, nextVertex, nextVertex.collapseNeighbor );
-
-		}
+		this.position = v;
 
-		//
+		this.id = id; // old index id
 
-		var simplifiedGeometry = new BufferGeometry();
-		var position = [];
-		var index = [];
+		this.faces = []; // faces vertex is connected
+		this.neighbors = []; // neighbouring vertices aka "adjacentVertices"
 
-		//
+		// these will be computed in computeEdgeCostAtVertex()
+		this.collapseCost = 0; // cost of collapsing this vertex, the less the better. aka objdist
+		this.collapseNeighbor = null; // best candinate for collapsing
 
-		for ( var i = 0; i < vertices.length; i ++ ) {
+	}
 
-			var vertex = vertices[ i ].position;
-			position.push( vertex.x, vertex.y, vertex.z );
+	addUniqueNeighbor( vertex ) {
 
-		}
+		pushIfUnique( this.neighbors, vertex );
 
-		//
+	}
 
-		for ( var i = 0; i < faces.length; i ++ ) {
+	removeIfNonNeighbor( n ) {
 
-			var face = faces[ i ];
+		const neighbors = this.neighbors;
+		const faces = this.faces;
 
-			var a = vertices.indexOf( face.v1 );
-			var b = vertices.indexOf( face.v2 );
-			var c = vertices.indexOf( face.v3 );
+		const offset = neighbors.indexOf( n );
 
-			index.push( a, b, c );
+		if ( offset === - 1 ) return;
 
-		}
+		for ( let i = 0; i < faces.length; i ++ ) {
 
-		//
+			if ( faces[ i ].hasVertex( n ) ) return;
 
-		simplifiedGeometry.setAttribute( 'position', new Float32BufferAttribute( position, 3 ) );
-		simplifiedGeometry.setIndex( index );
+		}
 
-		return simplifiedGeometry;
+		neighbors.splice( offset, 1 );
 
-	};
+	}
 
-} )();
+}
 
 export { SimplifyModifier };

+ 207 - 203
examples/jsm/modifiers/TessellateModifier.js

@@ -10,301 +10,305 @@ import {
  * Break faces with edges longer than maxEdgeLength
  */
 
-var TessellateModifier = function ( maxEdgeLength = 0.1, maxIterations = 6 ) {
+class TessellateModifier {
 
-	this.maxEdgeLength = maxEdgeLength;
-	this.maxIterations = maxIterations;
+	constructor( maxEdgeLength = 0.1, maxIterations = 6 ) {
 
-};
+		this.maxEdgeLength = maxEdgeLength;
+		this.maxIterations = maxIterations;
 
-TessellateModifier.prototype.modify = function ( geometry ) {
+	}
 
-	if ( geometry.isGeometry === true ) {
+	modify( geometry ) {
 
-		console.error( 'THREE.TessellateModifier no longer supports Geometry. Use BufferGeometry instead.' );
-		return geometry;
+		if ( geometry.isGeometry === true ) {
 
-	}
+			console.error( 'THREE.TessellateModifier no longer supports Geometry. Use BufferGeometry instead.' );
+			return geometry;
 
-	if ( geometry.index !== null ) {
+		}
 
-		geometry = geometry.toNonIndexed();
+		if ( geometry.index !== null ) {
 
-	}
+			geometry = geometry.toNonIndexed();
 
-	//
-
-	const maxIterations = this.maxIterations;
-	const maxEdgeLengthSquared = this.maxEdgeLength * this.maxEdgeLength;
-
-	const va = new Vector3();
-	const vb = new Vector3();
-	const vc = new Vector3();
-	const vm = new Vector3();
-	const vs = [ va, vb, vc, vm ];
-
-	const na = new Vector3();
-	const nb = new Vector3();
-	const nc = new Vector3();
-	const nm = new Vector3();
-	const ns = [ na, nb, nc, nm ];
-
-	const ca = new Color();
-	const cb = new Color();
-	const cc = new Color();
-	const cm = new Color();
-	const cs = [ ca, cb, cc, cm ];
-
-	const ua = new Vector2();
-	const ub = new Vector2();
-	const uc = new Vector2();
-	const um = new Vector2();
-	const us = [ ua, ub, uc, um ];
-
-	const u2a = new Vector2();
-	const u2b = new Vector2();
-	const u2c = new Vector2();
-	const u2m = new Vector2();
-	const u2s = [ u2a, u2b, u2c, u2m ];
-
-	const attributes = geometry.attributes;
-	const hasNormals = attributes.normal !== undefined;
-	const hasColors = attributes.color !== undefined;
-	const hasUVs = attributes.uv !== undefined;
-	const hasUV2s = attributes.uv2 !== undefined;
-
-	let positions = attributes.position.array;
-	let normals = hasNormals ? attributes.normal.array : null;
-	let colors = hasColors ? attributes.color.array : null;
-	let uvs = hasUVs ? attributes.uv.array : null;
-	let uv2s = hasUV2s ? attributes.uv2.array : null;
-
-	let positions2 = positions;
-	let normals2 = normals;
-	let colors2 = colors;
-	let uvs2 = uvs;
-	let uv2s2 = uv2s;
-
-	let iteration = 0;
-	let tessellating = true;
-
-	function addTriangle( a, b, c ) {
-
-		const v1 = vs[ a ];
-		const v2 = vs[ b ];
-		const v3 = vs[ c ];
-
-		positions2.push( v1.x, v1.y, v1.z );
-		positions2.push( v2.x, v2.y, v2.z );
-		positions2.push( v3.x, v3.y, v3.z );
+		}
 
-		if ( hasNormals ) {
+		//
+
+		const maxIterations = this.maxIterations;
+		const maxEdgeLengthSquared = this.maxEdgeLength * this.maxEdgeLength;
+
+		const va = new Vector3();
+		const vb = new Vector3();
+		const vc = new Vector3();
+		const vm = new Vector3();
+		const vs = [ va, vb, vc, vm ];
+
+		const na = new Vector3();
+		const nb = new Vector3();
+		const nc = new Vector3();
+		const nm = new Vector3();
+		const ns = [ na, nb, nc, nm ];
+
+		const ca = new Color();
+		const cb = new Color();
+		const cc = new Color();
+		const cm = new Color();
+		const cs = [ ca, cb, cc, cm ];
+
+		const ua = new Vector2();
+		const ub = new Vector2();
+		const uc = new Vector2();
+		const um = new Vector2();
+		const us = [ ua, ub, uc, um ];
+
+		const u2a = new Vector2();
+		const u2b = new Vector2();
+		const u2c = new Vector2();
+		const u2m = new Vector2();
+		const u2s = [ u2a, u2b, u2c, u2m ];
+
+		const attributes = geometry.attributes;
+		const hasNormals = attributes.normal !== undefined;
+		const hasColors = attributes.color !== undefined;
+		const hasUVs = attributes.uv !== undefined;
+		const hasUV2s = attributes.uv2 !== undefined;
+
+		let positions = attributes.position.array;
+		let normals = hasNormals ? attributes.normal.array : null;
+		let colors = hasColors ? attributes.color.array : null;
+		let uvs = hasUVs ? attributes.uv.array : null;
+		let uv2s = hasUV2s ? attributes.uv2.array : null;
+
+		let positions2 = positions;
+		let normals2 = normals;
+		let colors2 = colors;
+		let uvs2 = uvs;
+		let uv2s2 = uv2s;
+
+		let iteration = 0;
+		let tessellating = true;
+
+		function addTriangle( a, b, c ) {
+
+			const v1 = vs[ a ];
+			const v2 = vs[ b ];
+			const v3 = vs[ c ];
+
+			positions2.push( v1.x, v1.y, v1.z );
+			positions2.push( v2.x, v2.y, v2.z );
+			positions2.push( v3.x, v3.y, v3.z );
 
-			const n1 = ns[ a ];
-			const n2 = ns[ b ];
-			const n3 = ns[ c ];
+			if ( hasNormals ) {
 
-			normals2.push( n1.x, n1.y, n1.z );
-			normals2.push( n2.x, n2.y, n2.z );
-			normals2.push( n3.x, n3.y, n3.z );
+				const n1 = ns[ a ];
+				const n2 = ns[ b ];
+				const n3 = ns[ c ];
 
-		}
+				normals2.push( n1.x, n1.y, n1.z );
+				normals2.push( n2.x, n2.y, n2.z );
+				normals2.push( n3.x, n3.y, n3.z );
 
-		if ( hasColors ) {
+			}
 
-			const c1 = cs[ a ];
-			const c2 = cs[ b ];
-			const c3 = cs[ c ];
+			if ( hasColors ) {
 
-			colors2.push( c1.x, c1.y, c1.z );
-			colors2.push( c2.x, c2.y, c2.z );
-			colors2.push( c3.x, c3.y, c3.z );
+				const c1 = cs[ a ];
+				const c2 = cs[ b ];
+				const c3 = cs[ c ];
 
-		}
+				colors2.push( c1.x, c1.y, c1.z );
+				colors2.push( c2.x, c2.y, c2.z );
+				colors2.push( c3.x, c3.y, c3.z );
 
-		if ( hasUVs ) {
+			}
 
-			const u1 = us[ a ];
-			const u2 = us[ b ];
-			const u3 = us[ c ];
+			if ( hasUVs ) {
 
-			uvs2.push( u1.x, u1.y );
-			uvs2.push( u2.x, u2.y );
-			uvs2.push( u3.x, u3.y );
+				const u1 = us[ a ];
+				const u2 = us[ b ];
+				const u3 = us[ c ];
 
-		}
+				uvs2.push( u1.x, u1.y );
+				uvs2.push( u2.x, u2.y );
+				uvs2.push( u3.x, u3.y );
 
-		if ( hasUV2s ) {
+			}
 
-			const u21 = u2s[ a ];
-			const u22 = u2s[ b ];
-			const u23 = u2s[ c ];
+			if ( hasUV2s ) {
+
+				const u21 = u2s[ a ];
+				const u22 = u2s[ b ];
+				const u23 = u2s[ c ];
 
-			uv2s2.push( u21.x, u21.y );
-			uv2s2.push( u22.x, u22.y );
-			uv2s2.push( u23.x, u23.y );
+				uv2s2.push( u21.x, u21.y );
+				uv2s2.push( u22.x, u22.y );
+				uv2s2.push( u23.x, u23.y );
+
+			}
 
 		}
 
-	}
+		while ( tessellating && iteration < maxIterations ) {
 
-	while ( tessellating && iteration < maxIterations ) {
+			iteration ++;
+			tessellating = false;
 
-		iteration ++;
-		tessellating = false;
+			positions = positions2;
+			positions2 = [];
 
-		positions = positions2;
-		positions2 = [];
+			if ( hasNormals ) {
 
-		if ( hasNormals ) {
+				normals = normals2;
+				normals2 = [];
 
-			normals = normals2;
-			normals2 = [];
+			}
 
-		}
+			if ( hasColors ) {
 
-		if ( hasColors ) {
+				colors = colors2;
+				colors2 = [];
 
-			colors = colors2;
-			colors2 = [];
+			}
 
-		}
+			if ( hasUVs ) {
 
-		if ( hasUVs ) {
+				uvs = uvs2;
+				uvs2 = [];
 
-			uvs = uvs2;
-			uvs2 = [];
+			}
 
-		}
+			if ( hasUV2s ) {
 
-		if ( hasUV2s ) {
+				uv2s = uv2s2;
+				uv2s2 = [];
 
-			uv2s = uv2s2;
-			uv2s2 = [];
+			}
 
-		}
+			for ( let i = 0, i2 = 0, il = positions.length; i < il; i += 9, i2 += 6 ) {
 
-		for ( var i = 0, i2 = 0, il = positions.length; i < il; i += 9, i2 += 6 ) {
+				va.fromArray( positions, i + 0 );
+				vb.fromArray( positions, i + 3 );
+				vc.fromArray( positions, i + 6 );
 
-			va.fromArray( positions, i + 0 );
-			vb.fromArray( positions, i + 3 );
-			vc.fromArray( positions, i + 6 );
+				if ( hasNormals ) {
 
-			if ( hasNormals ) {
+					na.fromArray( normals, i + 0 );
+					nb.fromArray( normals, i + 3 );
+					nc.fromArray( normals, i + 6 );
 
-				na.fromArray( normals, i + 0 );
-				nb.fromArray( normals, i + 3 );
-				nc.fromArray( normals, i + 6 );
+				}
 
-			}
+				if ( hasColors ) {
 
-			if ( hasColors ) {
+					ca.fromArray( colors, i + 0 );
+					cb.fromArray( colors, i + 3 );
+					cc.fromArray( colors, i + 6 );
 
-				ca.fromArray( colors, i + 0 );
-				cb.fromArray( colors, i + 3 );
-				cc.fromArray( colors, i + 6 );
+				}
 
-			}
+				if ( hasUVs ) {
 
-			if ( hasUVs ) {
+					ua.fromArray( uvs, i2 + 0 );
+					ub.fromArray( uvs, i2 + 2 );
+					uc.fromArray( uvs, i2 + 4 );
 
-				ua.fromArray( uvs, i2 + 0 );
-				ub.fromArray( uvs, i2 + 2 );
-				uc.fromArray( uvs, i2 + 4 );
+				}
 
-			}
+				if ( hasUV2s ) {
 
-			if ( hasUV2s ) {
+					u2a.fromArray( uv2s, i2 + 0 );
+					u2b.fromArray( uv2s, i2 + 2 );
+					u2c.fromArray( uv2s, i2 + 4 );
 
-				u2a.fromArray( uv2s, i2 + 0 );
-				u2b.fromArray( uv2s, i2 + 2 );
-				u2c.fromArray( uv2s, i2 + 4 );
+				}
 
-			}
+				const dab = va.distanceToSquared( vb );
+				const dbc = vb.distanceToSquared( vc );
+				const dac = va.distanceToSquared( vc );
 
-			const dab = va.distanceToSquared( vb );
-			const dbc = vb.distanceToSquared( vc );
-			const dac = va.distanceToSquared( vc );
+				if ( dab > maxEdgeLengthSquared || dbc > maxEdgeLengthSquared || dac > maxEdgeLengthSquared ) {
 
-			if ( dab > maxEdgeLengthSquared || dbc > maxEdgeLengthSquared || dac > maxEdgeLengthSquared ) {
+					tessellating = true;
 
-				tessellating = true;
+					if ( dab >= dbc && dab >= dac ) {
 
-				if ( dab >= dbc && dab >= dac ) {
+						vm.lerpVectors( va, vb, 0.5 );
+						if ( hasNormals ) nm.lerpVectors( na, nb, 0.5 );
+						if ( hasColors ) cm.lerpColors( ca, cb, 0.5 );
+						if ( hasUVs ) um.lerpVectors( ua, ub, 0.5 );
+						if ( hasUV2s ) u2m.lerpVectors( u2a, u2b, 0.5 );
 
-					vm.lerpVectors( va, vb, 0.5 );
-					if ( hasNormals ) nm.lerpVectors( na, nb, 0.5 );
-					if ( hasColors ) cm.lerpColors( ca, cb, 0.5 );
-					if ( hasUVs ) um.lerpVectors( ua, ub, 0.5 );
-					if ( hasUV2s ) u2m.lerpVectors( u2a, u2b, 0.5 );
+						addTriangle( 0, 3, 2 );
+						addTriangle( 3, 1, 2 );
 
-					addTriangle( 0, 3, 2 );
-					addTriangle( 3, 1, 2 );
+					} else if ( dbc >= dab && dbc >= dac ) {
 
-				} else if ( dbc >= dab && dbc >= dac ) {
+						vm.lerpVectors( vb, vc, 0.5 );
+						if ( hasNormals ) nm.lerpVectors( nb, nc, 0.5 );
+						if ( hasColors ) cm.lerpColors( cb, cc, 0.5 );
+						if ( hasUVs ) um.lerpVectors( ub, uc, 0.5 );
+						if ( hasUV2s ) u2m.lerpVectors( u2b, u2c, 0.5 );
 
-					vm.lerpVectors( vb, vc, 0.5 );
-					if ( hasNormals ) nm.lerpVectors( nb, nc, 0.5 );
-					if ( hasColors ) cm.lerpColors( cb, cc, 0.5 );
-					if ( hasUVs ) um.lerpVectors( ub, uc, 0.5 );
-					if ( hasUV2s ) u2m.lerpVectors( u2b, u2c, 0.5 );
+						addTriangle( 0, 1, 3 );
+						addTriangle( 3, 2, 0 );
 
-					addTriangle( 0, 1, 3 );
-					addTriangle( 3, 2, 0 );
+					} else {
 
-				} else {
+						vm.lerpVectors( va, vc, 0.5 );
+						if ( hasNormals ) nm.lerpVectors( na, nc, 0.5 );
+						if ( hasColors ) cm.lerpColors( ca, cc, 0.5 );
+						if ( hasUVs ) um.lerpVectors( ua, uc, 0.5 );
+						if ( hasUV2s ) u2m.lerpVectors( u2a, u2c, 0.5 );
 
-					vm.lerpVectors( va, vc, 0.5 );
-					if ( hasNormals ) nm.lerpVectors( na, nc, 0.5 );
-					if ( hasColors ) cm.lerpColors( ca, cc, 0.5 );
-					if ( hasUVs ) um.lerpVectors( ua, uc, 0.5 );
-					if ( hasUV2s ) u2m.lerpVectors( u2a, u2c, 0.5 );
+						addTriangle( 0, 1, 3 );
+						addTriangle( 3, 1, 2 );
 
-					addTriangle( 0, 1, 3 );
-					addTriangle( 3, 1, 2 );
+					}
 
-				}
+				} else {
 
-			} else {
+					addTriangle( 0, 1, 2 );
 
-				addTriangle( 0, 1, 2 );
+				}
 
 			}
 
 		}
 
-	}
+		const geometry2 = new BufferGeometry();
 
-	const geometry2 = new BufferGeometry();
+		geometry2.setAttribute( 'position', new Float32BufferAttribute( positions2, 3 ) );
 
-	geometry2.setAttribute( 'position', new Float32BufferAttribute( positions2, 3 ) );
+		if ( hasNormals ) {
 
-	if ( hasNormals ) {
+			geometry2.setAttribute( 'normal', new Float32BufferAttribute( normals2, 3 ) );
 
-		geometry2.setAttribute( 'normal', new Float32BufferAttribute( normals2, 3 ) );
+		}
 
-	}
+		if ( hasColors ) {
 
-	if ( hasColors ) {
+			geometry2.setAttribute( 'color', new Float32BufferAttribute( colors2, 3 ) );
 
-		geometry2.setAttribute( 'color', new Float32BufferAttribute( colors2, 3 ) );
+		}
 
-	}
+		if ( hasUVs ) {
 
-	if ( hasUVs ) {
+			geometry2.setAttribute( 'uv', new Float32BufferAttribute( uvs2, 2 ) );
 
-		geometry2.setAttribute( 'uv', new Float32BufferAttribute( uvs2, 2 ) );
+		}
 
-	}
+		if ( hasUV2s ) {
+
+			geometry2.setAttribute( 'uv2', new Float32BufferAttribute( uv2s2, 2 ) );
 
-	if ( hasUV2s ) {
+		}
 
-		geometry2.setAttribute( 'uv2', new Float32BufferAttribute( uv2s2, 2 ) );
+		return geometry2;
 
 	}
 
-	return geometry2;
-
-};
+}
 
 export { TessellateModifier };