Browse Source

Smooth normals

Garrett Johnson 6 years ago
parent
commit
de028979df
1 changed files with 258 additions and 50 deletions
  1. 258 50
      examples/js/loaders/LDrawLoader.js

+ 258 - 50
examples/js/loaders/LDrawLoader.js

@@ -7,6 +7,165 @@
 
 
 THREE.LDrawLoader = ( function () {
 THREE.LDrawLoader = ( function () {
 
 
+	var tempVec0 = new THREE.Vector3();
+	var tempVec1 = new THREE.Vector3();
+	function smoothNormals( triangles, lineSegments ) {
+
+		function hashVertex( v ) {
+
+			var x = ~ ~ ( v.x * 1e6 );
+			var y = ~ ~ ( v.y * 1e6 );
+			var z = ~ ~ ( v.z * 1e6 );
+			return `${ x },${ y },${ z }`;
+
+		}
+
+		function hashEdge( v0, v1 ) {
+
+			return `${ hashVertex( v0 ) }_${ hashVertex( v1 ) }`;
+
+		}
+
+		var hardEdges = new Set();
+		var halfEdgeList = {};
+		var fullHalfEdgeList = {};
+		var normals = [];
+
+		// Save the list of hard edges by hash
+		for ( var i = 0, l = lineSegments.length; i < l; i ++ ) {
+
+			var ls = lineSegments[ i ];
+			var v0 = ls.v0;
+			var v1 = ls.v1;
+			hardEdges.add( hashEdge( v0, v1 ) );
+			hardEdges.add( hashEdge( v1, v0 ) );
+
+		}
+
+		// track the half edges associated with each triangle
+		for ( var i = 0, l = triangles.length; i < l; i ++ ) {
+
+			var tri = triangles[ i ];
+			for ( var i2 = 0, l2 = 3; i2 < l2; i2 ++ ) {
+
+				var index = i2;
+				var next = ( i2 + 1 ) % 3;
+				var v0 = tri[ `v${ index }` ];
+				var v1 = tri[ `v${ next }` ];
+				var hash = hashEdge( v0, v1 );
+
+				// don't add the triangle if the edge is supposed to be hard
+				if ( hardEdges.has( hash ) ) continue;
+				halfEdgeList[ hash ] = tri;
+				fullHalfEdgeList[ hash ] = tri;
+
+			}
+
+		}
+
+		// Iterate until we've tried to connect all triangles to share normals
+		while ( true ) {
+
+			// Stop if there are no more triangles left
+			var halfEdges = Object.keys( halfEdgeList );
+			if ( halfEdges.length === 0 ) break;
+
+			// Exhaustively find all connected triangles
+			var i = 0;
+			var queue = [ fullHalfEdgeList[ halfEdges[ 0 ] ] ];
+			while ( i < queue.length ) {
+
+				// initialize all vertex normals in this triangle
+				var tri = queue[ i ];
+				i ++;
+
+				var faceNormal = tri.faceNormal;
+				if ( tri.n0 === null ) {
+
+					tri.n0 = faceNormal.clone();
+					normals.push( tri.n0 );
+
+				}
+
+				if ( tri.n1 === null ) {
+
+					tri.n1 = faceNormal.clone();
+					normals.push( tri.n1 );
+
+				}
+
+				if ( tri.n2 === null ) {
+
+					tri.n2 = faceNormal.clone();
+					normals.push( tri.n2 );
+
+				}
+
+				// Check if any edge is connected to another triangle edge
+				for ( var i2 = 0, l2 = 3; i2 < l2; i2 ++ ) {
+
+					var index = i2;
+					var next = ( i2 + 1 ) % 3;
+					var v0 = tri[ `v${ index }` ];
+					var v1 = tri[ `v${ next }` ];
+
+					// delete this triangle from the list so it won't be found again
+					var hash = hashEdge( v0, v1 );
+					delete halfEdgeList[ hash ];
+
+					var reverseHash = hashEdge( v1, v0 );
+					var otherTri = fullHalfEdgeList[ reverseHash ];
+					if ( otherTri ) {
+
+						// if this triangle has already been traversed then it won't be in
+						// the halfEdgeList. If it has not then add it to the queue and delete
+						// it so it won't be found again.
+						if ( reverseHash in halfEdgeList ) {
+
+							queue.push( otherTri );
+							delete halfEdgeList[ reverseHash ];
+
+						}
+
+						// Find the matching edge in this triangle and copy the normal vector over
+						for ( var i3 = 0, l3 = 3; i3 < l3; i3 ++ ) {
+
+							var otherIndex = i3;
+							var otherNext = ( i3 + 1 ) % 3;
+							var otherV0 = otherTri[ `v${ otherIndex }` ];
+							var otherV1 = otherTri[ `v${ otherNext }` ];
+
+							var otherHash = hashEdge( otherV0, otherV1 );
+							if ( otherHash === reverseHash ) {
+
+								otherTri[ `n${ otherIndex }` ] = tri[ `n${ next }` ];
+								otherTri[ `n${ otherNext }` ] = tri[ `n${ index }` ];
+
+								tri[ `n${ next }` ].add( otherTri.faceNormal );
+								tri[ `n${ index }` ].add( otherTri.faceNormal );
+								break;
+
+							}
+
+						}
+
+					}
+
+				}
+
+			}
+
+		}
+
+		// The normals of each face have been added up so now we average them by normalizing the vector.
+		for ( var i = 0, l = normals.length; i < l; i ++ ) {
+
+			normals[ i ].normalize();
+
+		}
+
+	}
+
 	function isPrimitiveType( type ) {
 	function isPrimitiveType( type ) {
 
 
 		return /primitive/i.test( type ) || type === 'Subpart';
 		return /primitive/i.test( type ) || type === 'Subpart';
@@ -125,11 +284,11 @@ THREE.LDrawLoader = ( function () {
 		// Sort the triangles or line segments by colour code to make later the mesh groups
 		// Sort the triangles or line segments by colour code to make later the mesh groups
 		elements.sort( sortByMaterial );
 		elements.sort( sortByMaterial );
 
 
-		var vertices = [];
+		var positions = [];
+		var normals = [];
 		var materials = [];
 		var materials = [];
 
 
 		var bufferGeometry = new THREE.BufferGeometry();
 		var bufferGeometry = new THREE.BufferGeometry();
-		bufferGeometry.clearGroups();
 		var prevMaterial = null;
 		var prevMaterial = null;
 		var index0 = 0;
 		var index0 = 0;
 		var numGroupVerts = 0;
 		var numGroupVerts = 0;
@@ -140,10 +299,17 @@ THREE.LDrawLoader = ( function () {
 			var v0 = elem.v0;
 			var v0 = elem.v0;
 			var v1 = elem.v1;
 			var v1 = elem.v1;
 			// Note that LDraw coordinate system is rotated 180 deg. in the X axis w.r.t. Three.js's one
 			// Note that LDraw coordinate system is rotated 180 deg. in the X axis w.r.t. Three.js's one
-			vertices.push( v0.x, v0.y, v0.z, v1.x, v1.y, v1.z );
+			positions.push( v0.x, v0.y, v0.z, v1.x, v1.y, v1.z );
 			if ( elementSize === 3 ) {
 			if ( elementSize === 3 ) {
 
 
-				vertices.push( elem.v2.x, elem.v2.y, elem.v2.z );
+				positions.push( elem.v2.x, elem.v2.y, elem.v2.z );
+
+				var n0 = elem.n0 || elem.faceNormal;
+				var n1 = elem.n1 || elem.faceNormal;
+				var n2 = elem.n2 || elem.faceNormal;
+				normals.push( n0.x, n0.y, n0.z );
+				normals.push( n1.x, n1.y, n1.z );
+				normals.push( n2.x, n2.y, n2.z );
 
 
 			}
 			}
 
 
@@ -175,7 +341,13 @@ THREE.LDrawLoader = ( function () {
 
 
 		}
 		}
 
 
-		bufferGeometry.addAttribute( 'position', new THREE.Float32BufferAttribute( vertices, 3 ) );
+		bufferGeometry.addAttribute( 'position', new THREE.Float32BufferAttribute( positions, 3 ) );
+
+		if ( elementSize === 3 ) {
+
+			bufferGeometry.addAttribute( 'normal', new THREE.Float32BufferAttribute( normals, 3 ) );
+
+		}
 
 
 		var object3d = null;
 		var object3d = null;
 
 
@@ -185,8 +357,6 @@ THREE.LDrawLoader = ( function () {
 
 
 		} else if ( elementSize === 3 ) {
 		} else if ( elementSize === 3 ) {
 
 
-			bufferGeometry.computeVertexNormals();
-
 			object3d = new THREE.Mesh( bufferGeometry, materials );
 			object3d = new THREE.Mesh( bufferGeometry, materials );
 
 
 		}
 		}
@@ -338,10 +508,16 @@ THREE.LDrawLoader = ( function () {
 
 
 				function finalizeObject() {
 				function finalizeObject() {
 
 
-					// TODO: Handle smoothing
+					if ( parseScope.type === 'Part' ) {
+
+						smoothNormals( parseScope.triangles, parseScope.lineSegments );
+
+					}
+
 					var isRoot = ! parentParseScope.isFromParse;
 					var isRoot = ! parentParseScope.isFromParse;
 					if ( scope.separateObjects && ! isPrimitiveType( parseScope.type ) || isRoot ) {
 					if ( scope.separateObjects && ! isPrimitiveType( parseScope.type ) || isRoot ) {
 
 
+
 						const objGroup = parseScope.groupObject;
 						const objGroup = parseScope.groupObject;
 						if ( parseScope.triangles.length > 0 ) {
 						if ( parseScope.triangles.length > 0 ) {
 
 
@@ -374,36 +550,7 @@ THREE.LDrawLoader = ( function () {
 
 
 					} else {
 					} else {
 
 
-						if ( scope.separateObjects ) {
-
-							parseScope.lineSegments.forEach( ls => {
-
-								ls.v0.applyMatrix4( parseScope.matrix );
-								ls.v1.applyMatrix4( parseScope.matrix );
-
-							} );
-
-							parseScope.optionalSegments.forEach( ls => {
-
-								ls.v0.applyMatrix4( parseScope.matrix );
-								ls.v1.applyMatrix4( parseScope.matrix );
-
-							} );
-
-							parseScope.triangles.forEach( ls => {
-
-								ls.v0 = ls.v0.clone().applyMatrix4( parseScope.matrix );
-								ls.v1 = ls.v1.clone().applyMatrix4( parseScope.matrix );
-								ls.v2 = ls.v2.clone().applyMatrix4( parseScope.matrix );
-
-							} );
-
-						}
-
-
-						// TODO: we need to multiple matrices here
-						// TODO: First, instead of tracking matrices anywhere else we
-						// should just multiple everything here.
+						var separateObjects = scope.separateObjects;
 						var parentLineSegments = parentParseScope.lineSegments;
 						var parentLineSegments = parentParseScope.lineSegments;
 						var parentOptionalSegments = parentParseScope.optionalSegments;
 						var parentOptionalSegments = parentParseScope.optionalSegments;
 						var parentTriangles = parentParseScope.triangles;
 						var parentTriangles = parentParseScope.triangles;
@@ -414,19 +561,45 @@ THREE.LDrawLoader = ( function () {
 
 
 						for ( var i = 0, l = lineSegments.length; i < l; i ++ ) {
 						for ( var i = 0, l = lineSegments.length; i < l; i ++ ) {
 
 
-							parentLineSegments.push( lineSegments[ i ] );
+							var ls = lineSegments[ i ];
+							if ( separateObjects ) {
+
+								ls.v0.applyMatrix4( parseScope.matrix );
+								ls.v1.applyMatrix4( parseScope.matrix );
+
+							}
+							parentLineSegments.push( ls );
 
 
 						}
 						}
 
 
 						for ( var i = 0, l = optionalSegments.length; i < l; i ++ ) {
 						for ( var i = 0, l = optionalSegments.length; i < l; i ++ ) {
 
 
-							parentOptionalSegments.push( optionalSegments[ i ] );
+							var os = optionalSegments[ i ];
+							if ( separateObjects ) {
+
+								os.v0.applyMatrix4( parseScope.matrix );
+								os.v1.applyMatrix4( parseScope.matrix );
+
+							}
+							parentOptionalSegments.push( os );
 
 
 						}
 						}
 
 
 						for ( var i = 0, l = triangles.length; i < l; i ++ ) {
 						for ( var i = 0, l = triangles.length; i < l; i ++ ) {
 
 
-							parentTriangles.push( triangles[ i ] );
+							var tri = triangles[ i ];
+							if ( separateObjects ) {
+
+								tri.v0 = tri.v0.clone().applyMatrix4( parseScope.matrix );
+								tri.v1 = tri.v1.clone().applyMatrix4( parseScope.matrix );
+								tri.v2 = tri.v2.clone().applyMatrix4( parseScope.matrix );
+
+								tempVec0.subVectors( tri.v1, tri.v0 );
+								tempVec1.subVectors( tri.v2, tri.v1 );
+								tri.faceNormal.crossVectors( tempVec0, tempVec1 ).normalize();
+
+							}
+							parentTriangles.push( tri );
 
 
 						}
 						}
 
 
@@ -1145,7 +1318,6 @@ THREE.LDrawLoader = ( function () {
 
 
 										}
 										}
 
 
-
 										triangles = currentParseScope.triangles;
 										triangles = currentParseScope.triangles;
 										lineSegments = currentParseScope.lineSegments;
 										lineSegments = currentParseScope.lineSegments;
 										optionalSegments = currentParseScope.optionalSegments;
 										optionalSegments = currentParseScope.optionalSegments;
@@ -1355,7 +1527,7 @@ THREE.LDrawLoader = ( function () {
 						var inverted = currentParseScope.inverted;
 						var inverted = currentParseScope.inverted;
 						var ccw = bfcCCW !== inverted;
 						var ccw = bfcCCW !== inverted;
 						var doubleSided = ! bfcCertified || ! bfcCull;
 						var doubleSided = ! bfcCertified || ! bfcCull;
-						var v0, v1, v2;
+						var v0, v1, v2, faceNormal;
 
 
 						if ( ccw === true ) {
 						if ( ccw === true ) {
 
 
@@ -1371,12 +1543,22 @@ THREE.LDrawLoader = ( function () {
 
 
 						}
 						}
 
 
+						tempVec0.subVectors( v1, v0 );
+						tempVec1.subVectors( v2, v1 );
+						faceNormal = new THREE.Vector3()
+							.crossVectors( tempVec0, tempVec1 )
+							.normalize();
+
 						triangles.push( {
 						triangles.push( {
 							material: material,
 							material: material,
 							colourCode: material.userData.code,
 							colourCode: material.userData.code,
 							v0: v0,
 							v0: v0,
 							v1: v1,
 							v1: v1,
-							v2: v2
+							v2: v2,
+							faceNormal: faceNormal,
+							n0: null,
+							n1: null,
+							n2: null
 						} );
 						} );
 
 
 						if ( doubleSided === true ) {
 						if ( doubleSided === true ) {
@@ -1386,7 +1568,11 @@ THREE.LDrawLoader = ( function () {
 								colourCode: material.userData.code,
 								colourCode: material.userData.code,
 								v0: v0,
 								v0: v0,
 								v1: v2,
 								v1: v2,
-								v2: v1
+								v2: v1,
+								faceNormal: faceNormal,
+								n0: null,
+								n1: null,
+								n2: null
 							} );
 							} );
 
 
 						}
 						}
@@ -1401,7 +1587,7 @@ THREE.LDrawLoader = ( function () {
 						var inverted = currentParseScope.inverted;
 						var inverted = currentParseScope.inverted;
 						var ccw = bfcCCW !== inverted;
 						var ccw = bfcCCW !== inverted;
 						var doubleSided = ! bfcCertified || ! bfcCull;
 						var doubleSided = ! bfcCertified || ! bfcCull;
-						var v0, v1, v2, v3;
+						var v0, v1, v2, v3, faceNormal;
 
 
 						if ( ccw === true ) {
 						if ( ccw === true ) {
 
 
@@ -1419,12 +1605,22 @@ THREE.LDrawLoader = ( function () {
 
 
 						}
 						}
 
 
+						tempVec0.subVectors( v1, v0 );
+						tempVec1.subVectors( v2, v1 );
+						faceNormal = new THREE.Vector3()
+							.crossVectors( tempVec0, tempVec1 )
+							.normalize();
+
 						triangles.push( {
 						triangles.push( {
 							material: material,
 							material: material,
 							colourCode: material.userData.code,
 							colourCode: material.userData.code,
 							v0: v0,
 							v0: v0,
 							v1: v1,
 							v1: v1,
-							v2: v2
+							v2: v2,
+							faceNormal: faceNormal,
+							n0: null,
+							n1: null,
+							n2: null
 						} );
 						} );
 
 
 						triangles.push( {
 						triangles.push( {
@@ -1432,7 +1628,11 @@ THREE.LDrawLoader = ( function () {
 							colourCode: material.userData.code,
 							colourCode: material.userData.code,
 							v0: v0,
 							v0: v0,
 							v1: v2,
 							v1: v2,
-							v2: v3
+							v2: v3,
+							faceNormal: faceNormal,
+							n0: null,
+							n1: null,
+							n2: null
 						} );
 						} );
 
 
 						if ( doubleSided === true ) {
 						if ( doubleSided === true ) {
@@ -1442,7 +1642,11 @@ THREE.LDrawLoader = ( function () {
 								colourCode: material.userData.code,
 								colourCode: material.userData.code,
 								v0: v0,
 								v0: v0,
 								v1: v2,
 								v1: v2,
-								v2: v1
+								v2: v1,
+								faceNormal: faceNormal,
+								n0: null,
+								n1: null,
+								n2: null
 							} );
 							} );
 
 
 							triangles.push( {
 							triangles.push( {
@@ -1450,7 +1654,11 @@ THREE.LDrawLoader = ( function () {
 								colourCode: material.userData.code,
 								colourCode: material.userData.code,
 								v0: v0,
 								v0: v0,
 								v1: v3,
 								v1: v3,
-								v2: v2
+								v2: v2,
+								faceNormal: faceNormal,
+								n0: null,
+								n1: null,
+								n2: null
 							} );
 							} );
 
 
 						}
 						}