Browse Source

LDrawLoader: Improve parts library ergonomics, improve normal smoothing functionality (#22249)

* Add "setPartsLibraryPath" function

* Add "preloadMaterials" function

* Use single material if possible

* Fix normal smoothing
Garrett Johnson 4 years ago
parent
commit
2739283b1e
1 changed files with 79 additions and 12 deletions
  1. 79 12
      examples/jsm/loaders/LDrawLoader.js

+ 79 - 12
examples/jsm/loaders/LDrawLoader.js

@@ -303,48 +303,71 @@ function smoothNormals( faces, lineSegments ) {
 
 
 					// share the first normal
 					// share the first normal
 					const otherNext = ( otherIndex + 1 ) % otherVertCount;
 					const otherNext = ( otherIndex + 1 ) % otherVertCount;
+					if (
+						vertNormals[ index ] && otherNormals[ otherNext ] &&
+						vertNormals[ index ] !== otherNormals[ otherNext ]
+					) {
+
+						otherNormals[ otherNext ].norm.add( vertNormals[ index ].norm );
+						vertNormals[ index ].norm = otherNormals[ otherNext ].norm;
+
+					}
+
 					let sharedNormal1 = vertNormals[ index ] || otherNormals[ otherNext ];
 					let sharedNormal1 = vertNormals[ index ] || otherNormals[ otherNext ];
 					if ( sharedNormal1 === null ) {
 					if ( sharedNormal1 === null ) {
 
 
-						sharedNormal1 = new Vector3();
-						normals.push( sharedNormal1 );
+						// it's possible to encounter an edge of a triangle that has already been traversed meaning
+						// both edges already have different normals defined and shared. To work around this we create
+						// a wrapper object so when those edges are merged the normals can be updated everywhere.
+						sharedNormal1 = { norm: new Vector3() };
+						normals.push( sharedNormal1.norm );
 
 
 					}
 					}
 
 
 					if ( vertNormals[ index ] === null ) {
 					if ( vertNormals[ index ] === null ) {
 
 
 						vertNormals[ index ] = sharedNormal1;
 						vertNormals[ index ] = sharedNormal1;
-						sharedNormal1.add( faceNormal );
+						sharedNormal1.norm.add( faceNormal );
 
 
 					}
 					}
 
 
 					if ( otherNormals[ otherNext ] === null ) {
 					if ( otherNormals[ otherNext ] === null ) {
 
 
 						otherNormals[ otherNext ] = sharedNormal1;
 						otherNormals[ otherNext ] = sharedNormal1;
-						sharedNormal1.add( otherFaceNormal );
+						sharedNormal1.norm.add( otherFaceNormal );
 
 
 					}
 					}
 
 
 					// share the second normal
 					// share the second normal
+					if (
+						vertNormals[ next ] && otherNormals[ otherIndex ] &&
+						vertNormals[ next ] !== otherNormals[ otherIndex ]
+					) {
+
+						otherNormals[ otherIndex ].norm.add( vertNormals[ next ].norm );
+						vertNormals[ next ].norm = otherNormals[ otherIndex ].norm;
+
+					}
+
 					let sharedNormal2 = vertNormals[ next ] || otherNormals[ otherIndex ];
 					let sharedNormal2 = vertNormals[ next ] || otherNormals[ otherIndex ];
 					if ( sharedNormal2 === null ) {
 					if ( sharedNormal2 === null ) {
 
 
-						sharedNormal2 = new Vector3();
-						normals.push( sharedNormal2 );
+						sharedNormal2 = { norm: new Vector3() };
+						normals.push( sharedNormal2.norm );
 
 
 					}
 					}
 
 
 					if ( vertNormals[ next ] === null ) {
 					if ( vertNormals[ next ] === null ) {
 
 
 						vertNormals[ next ] = sharedNormal2;
 						vertNormals[ next ] = sharedNormal2;
-						sharedNormal2.add( faceNormal );
+						sharedNormal2.norm.add( faceNormal );
 
 
 					}
 					}
 
 
 					if ( otherNormals[ otherIndex ] === null ) {
 					if ( otherNormals[ otherIndex ] === null ) {
 
 
 						otherNormals[ otherIndex ] = sharedNormal2;
 						otherNormals[ otherIndex ] = sharedNormal2;
-						sharedNormal2.add( otherFaceNormal );
+						sharedNormal2.norm.add( otherFaceNormal );
 
 
 					}
 					}
 
 
@@ -540,7 +563,13 @@ function createObject( elements, elementSize, isConditionalSegments = false, tot
 
 
 			for ( let j = 0, l = elemNormals.length; j < l; j ++ ) {
 			for ( let j = 0, l = elemNormals.length; j < l; j ++ ) {
 
 
-				const n = elemNormals[ j ] || elem.faceNormal;
+				let n = elem.faceNormal;
+				if ( elemNormals[ j ] ) {
+
+					n = elemNormals[ j ].norm;
+
+				}
+
 				const index = offset + j * 3;
 				const index = offset + j * 3;
 				normals[ index + 0 ] = n.x;
 				normals[ index + 0 ] = n.x;
 				normals[ index + 1 ] = n.y;
 				normals[ index + 1 ] = n.y;
@@ -592,11 +621,11 @@ function createObject( elements, elementSize, isConditionalSegments = false, tot
 
 
 	if ( elementSize === 2 ) {
 	if ( elementSize === 2 ) {
 
 
-		object3d = new LineSegments( bufferGeometry, materials );
+		object3d = new LineSegments( bufferGeometry, materials.length === 1 ? materials[ 0 ] : materials );
 
 
 	} else if ( elementSize === 3 ) {
 	} else if ( elementSize === 3 ) {
 
 
-		object3d = new Mesh( bufferGeometry, materials );
+		object3d = new Mesh( bufferGeometry, materials.length === 1 ? materials[ 0 ] : materials );
 
 
 	}
 	}
 
 
@@ -687,6 +716,44 @@ class LDrawLoader extends Loader {
 		// If this flag is set to true the vertex normals will be smoothed.
 		// If this flag is set to true the vertex normals will be smoothed.
 		this.smoothNormals = true;
 		this.smoothNormals = true;
 
 
+		// The path to load parts from the LDraw parts library from.
+		this.partsLibraryPath = '';
+
+	}
+
+	setPartsLibraryPath( path ) {
+
+		this.partsLibraryPath = path;
+		return this;
+
+	}
+
+	async preloadMaterials( url ) {
+
+		const fileLoader = new FileLoader( this.manager );
+		fileLoader.setPath( this.path );
+		fileLoader.setRequestHeader( this.requestHeader );
+		fileLoader.setWithCredentials( this.withCredentials );
+
+		const text = await fileLoader.loadAsync( url );
+		const colorLineRegex = /^0 !COLOUR/;
+		const lines = text.split( /[\n\r]/g );
+		const materials = [];
+		for ( let i = 0, l = lines.length; i < l; i ++ ) {
+
+			const line = lines[ i ];
+			if ( colorLineRegex.test( line ) ) {
+
+				const directive = line.replace( colorLineRegex, '' );
+				const material = this.parseColourMetaDirective( new LineParser( directive ) );
+				materials.push( material );
+
+			}
+
+		}
+
+		this.setMaterials( materials );
+
 	}
 	}
 
 
 	load( url, onLoad, onProgress, onError ) {
 	load( url, onLoad, onProgress, onError ) {
@@ -1988,7 +2055,7 @@ class LDrawLoader extends Loader {
 			// Use another file loader here so we can keep track of the subobject information
 			// Use another file loader here so we can keep track of the subobject information
 			// and use it when processing the next model.
 			// and use it when processing the next model.
 			const fileLoader = new FileLoader( scope.manager );
 			const fileLoader = new FileLoader( scope.manager );
-			fileLoader.setPath( scope.path );
+			fileLoader.setPath( scope.partsLibraryPath );
 			fileLoader.setRequestHeader( scope.requestHeader );
 			fileLoader.setRequestHeader( scope.requestHeader );
 			fileLoader.setWithCredentials( scope.withCredentials );
 			fileLoader.setWithCredentials( scope.withCredentials );
 			fileLoader.load( subobjectURL, function ( text ) {
 			fileLoader.load( subobjectURL, function ( text ) {