2
0
Эх сурвалжийг харах

Merge pull request #16183 from gkjohnson/ldraw-bfc

Add Back Face Culling Extension Support to LDrawLoader
Mr.doob 6 жил өмнө
parent
commit
7cc8d94606

+ 169 - 15
examples/js/loaders/LDrawLoader.js

@@ -271,7 +271,19 @@ THREE.LDrawLoader = ( function () {
 
 
 			}, onProgress, onError );
 			}, onProgress, onError );
 
 
-			function processObject( text, onProcessed ) {
+			function subobjectLoad( url, onLoad, onProgress, onError, subobject ) {
+
+				var fileLoader = new THREE.FileLoader( scope.manager );
+				fileLoader.setPath( scope.path );
+				fileLoader.load( url, function ( text ) {
+
+					processObject( text, onLoad, subobject );
+
+				}, onProgress, onError );
+
+			}
+
+			function processObject( text, onProcessed, subobject ) {
 
 
 				var parseScope = scope.newParseScopeLevel();
 				var parseScope = scope.newParseScopeLevel();
 				parseScope.url = url;
 				parseScope.url = url;
@@ -284,9 +296,10 @@ THREE.LDrawLoader = ( function () {
 
 
 					scope.subobjectCache[ currentFileName ] = text;
 					scope.subobjectCache[ currentFileName ] = text;
 
 
-
 				}
 				}
 
 
+				parseScope.inverted = subobject !== undefined ? subobject.inverted : false;
+
 				// Parse the object (returns a THREE.Group)
 				// Parse the object (returns a THREE.Group)
 				var objGroup = scope.parse( text );
 				var objGroup = scope.parse( text );
 
 
@@ -372,7 +385,7 @@ THREE.LDrawLoader = ( function () {
 					var cached = scope.subobjectCache[ subobject.originalFileName ];
 					var cached = scope.subobjectCache[ subobject.originalFileName ];
 					if ( cached ) {
 					if ( cached ) {
 
 
-						var subobjectGroup = processObject( cached, sync ? undefined : onSubobjectLoaded );
+						var subobjectGroup = processObject( cached, sync ? undefined : onSubobjectLoaded, subobject );
 						if ( sync ) {
 						if ( sync ) {
 
 
 							addSubobject( subobject, subobjectGroup );
 							addSubobject( subobject, subobjectGroup );
@@ -462,7 +475,15 @@ THREE.LDrawLoader = ( function () {
 					subobject.url = subobjectURL;
 					subobject.url = subobjectURL;
 
 
 					// Load the subobject
 					// Load the subobject
-					scope.load( subobjectURL, onSubobjectLoaded, undefined, onSubobjectError );
+					// Use another file loader here so we can keep track of the subobject information
+					// and use it when processing the next model.
+					var fileLoader = new THREE.FileLoader( scope.manager );
+					fileLoader.setPath( scope.path );
+					fileLoader.load( subobjectURL, function ( text ) {
+
+						processObject( text, onSubobjectLoaded, subobject );
+
+					}, undefined, onSubobjectError );
 
 
 				}
 				}
 
 
@@ -587,6 +608,7 @@ THREE.LDrawLoader = ( function () {
 				subobjects: null,
 				subobjects: null,
 				numSubobjects: 0,
 				numSubobjects: 0,
 				subobjectIndex: 0,
 				subobjectIndex: 0,
+				inverted: false,
 
 
 				// Current subobject
 				// Current subobject
 				currentFileName: null,
 				currentFileName: null,
@@ -897,9 +919,6 @@ THREE.LDrawLoader = ( function () {
 
 
 			}
 			}
 
 
-			// BFC (Back Face Culling) LDraw language meta extension is not implemented, so set all materials double-sided:
-			material.side = THREE.DoubleSide;
-
 			material.transparent = isTransparent;
 			material.transparent = isTransparent;
 			material.opacity = alpha;
 			material.opacity = alpha;
 
 
@@ -1035,6 +1054,11 @@ THREE.LDrawLoader = ( function () {
 
 
 			}
 			}
 
 
+			var bfcCertified = false;
+			var bfcCCW = true;
+			var bfcInverted = false;
+			var bfcCull = true;
+
 			// Parse all line commands
 			// Parse all line commands
 			for ( lineIndex = 0; lineIndex < numLines; lineIndex ++ ) {
 			for ( lineIndex = 0; lineIndex < numLines; lineIndex ++ ) {
 
 
@@ -1137,6 +1161,58 @@ THREE.LDrawLoader = ( function () {
 										currentEmbeddedFileName = lp.getRemainingString();
 										currentEmbeddedFileName = lp.getRemainingString();
 										currentEmbeddedText = '';
 										currentEmbeddedText = '';
 
 
+										bfcCertified = false;
+										bfcCCW = true;
+
+									}
+
+									break;
+
+								case 'BFC':
+
+									// Changes to the backface culling state
+									while ( ! lp.isAtTheEnd() ) {
+
+										var token = lp.getToken();
+
+										switch ( token ) {
+
+											case 'CERTIFY':
+											case 'NOCERTIFY':
+
+												bfcCertified = token === 'CERTIFY';
+												bfcCCW = true;
+
+												break;
+
+											case 'CW':
+											case 'CCW':
+
+												bfcCCW = token === 'CCW';
+
+												break;
+
+											case 'INVERTNEXT':
+
+												bfcInverted = true;
+
+												break;
+
+											case 'CLIP':
+											case 'NOCLIP':
+
+											  bfcCull = token === 'CLIP';
+
+												break;
+
+											default:
+
+												console.warn( 'THREE.LDrawLoader: BFC directive "' + token + '" is unknown.' );
+
+												break;
+
+										}
+
 									}
 									}
 
 
 									break;
 									break;
@@ -1198,6 +1274,14 @@ THREE.LDrawLoader = ( function () {
 
 
 						}
 						}
 
 
+						// If the scale of the object is negated then the triangle winding order
+						// needs to be flipped.
+						if ( scope.separateObjects === false && matrix.determinant() < 0 ) {
+
+							bfcInverted = ! bfcInverted;
+
+						}
+
 						subobjects.push( {
 						subobjects.push( {
 							material: material,
 							material: material,
 							matrix: matrix,
 							matrix: matrix,
@@ -1205,9 +1289,12 @@ THREE.LDrawLoader = ( function () {
 							originalFileName: fileName,
 							originalFileName: fileName,
 							locationState: LDrawLoader.FILE_LOCATION_AS_IS,
 							locationState: LDrawLoader.FILE_LOCATION_AS_IS,
 							url: null,
 							url: null,
-							triedLowerCase: false
+							triedLowerCase: false,
+							inverted: bfcInverted !== currentParseScope.inverted
 						} );
 						} );
 
 
+						bfcInverted = false;
+
 						break;
 						break;
 
 
 					// Line type 2: Line segment
 					// Line type 2: Line segment
@@ -1229,14 +1316,45 @@ THREE.LDrawLoader = ( function () {
 
 
 						var material = parseColourCode( lp );
 						var material = parseColourCode( lp );
 
 
+						var inverted = currentParseScope.inverted;
+						var ccw = bfcCCW !== inverted;
+						var doubleSided = ! bfcCertified || ! bfcCull;
+						var v0, v1, v2;
+
+						if ( ccw === true ) {
+
+							v0 = parseVector( lp );
+							v1 = parseVector( lp );
+							v2 = parseVector( lp );
+
+						} else {
+
+							v2 = parseVector( lp );
+							v1 = parseVector( lp );
+							v0 = parseVector( lp );
+
+						}
+
 						triangles.push( {
 						triangles.push( {
 							material: material,
 							material: material,
 							colourCode: material.userData.code,
 							colourCode: material.userData.code,
-							v0: parseVector( lp ),
-							v1: parseVector( lp ),
-							v2: parseVector( lp )
+							v0: v0,
+							v1: v1,
+							v2: v2
 						} );
 						} );
 
 
+						if ( doubleSided === true ) {
+
+							triangles.push( {
+								material: material,
+								colourCode: material.userData.code,
+								v0: v0,
+								v1: v2,
+								v2: v1
+							} );
+
+						}
+
 						break;
 						break;
 
 
 					// Line type 4: Quadrilateral
 					// Line type 4: Quadrilateral
@@ -1244,10 +1362,26 @@ THREE.LDrawLoader = ( function () {
 
 
 						var material = parseColourCode( lp );
 						var material = parseColourCode( lp );
 
 
-						var v0 = parseVector( lp );
-						var v1 = parseVector( lp );
-						var v2 = parseVector( lp );
-						var v3 = parseVector( lp );
+						var inverted = currentParseScope.inverted;
+						var ccw = bfcCCW !== inverted;
+						var doubleSided = ! bfcCertified || ! bfcCull;
+						var v0, v1, v2, v3;
+
+						if ( ccw === true ) {
+
+							v0 = parseVector( lp );
+							v1 = parseVector( lp );
+							v2 = parseVector( lp );
+							v3 = parseVector( lp );
+
+						} else {
+
+							v3 = parseVector( lp );
+							v2 = parseVector( lp );
+							v1 = parseVector( lp );
+							v0 = parseVector( lp );
+
+						}
 
 
 						triangles.push( {
 						triangles.push( {
 							material: material,
 							material: material,
@@ -1265,6 +1399,26 @@ THREE.LDrawLoader = ( function () {
 							v2: v3
 							v2: v3
 						} );
 						} );
 
 
+						if ( doubleSided === true ) {
+
+							triangles.push( {
+								material: material,
+								colourCode: material.userData.code,
+								v0: v0,
+								v1: v2,
+								v2: v1
+							} );
+
+							triangles.push( {
+								material: material,
+								colourCode: material.userData.code,
+								v0: v0,
+								v1: v3,
+								v2: v2
+							} );
+
+						}
+
 						break;
 						break;
 
 
 					// Line type 5: Optional line
 					// Line type 5: Optional line

+ 10 - 2
examples/webgl_loader_ldraw.html

@@ -97,7 +97,7 @@
 
 
 				//
 				//
 
 
-				renderer = new THREE.WebGLRenderer();
+				renderer = new THREE.WebGLRenderer( { antialias: true } );
 				renderer.setPixelRatio( window.devicePixelRatio );
 				renderer.setPixelRatio( window.devicePixelRatio );
 				renderer.setSize( window.innerWidth, window.innerHeight );
 				renderer.setSize( window.innerWidth, window.innerHeight );
 				container.appendChild( renderer.domElement );
 				container.appendChild( renderer.domElement );
@@ -108,7 +108,8 @@
 
 
 				guiData = {
 				guiData = {
 					modelFileName: modelFileList[ 'Car' ],
 					modelFileName: modelFileList[ 'Car' ],
-					envMapActivated: false
+					envMapActivated: false,
+					separateObjects: false
 				};
 				};
 
 
 				gui = new dat.GUI();
 				gui = new dat.GUI();
@@ -127,6 +128,12 @@
 
 
 				} );
 				} );
 
 
+				gui.add( guiData, 'separateObjects' ).name( 'Separate Objects' ).onChange( function ( value ) {
+
+					reloadObject( false );
+
+				} );
+
 				window.addEventListener( 'resize', onWindowResize, false );
 				window.addEventListener( 'resize', onWindowResize, false );
 
 
 				progressBarDiv = document.createElement( 'div' );
 				progressBarDiv = document.createElement( 'div' );
@@ -160,6 +167,7 @@
 				showProgressBar();
 				showProgressBar();
 
 
 				var lDrawLoader = new THREE.LDrawLoader();
 				var lDrawLoader = new THREE.LDrawLoader();
+				lDrawLoader.separateObjects = guiData.separateObjects;
 				lDrawLoader
 				lDrawLoader
 					.setPath( ldrawPath )
 					.setPath( ldrawPath )
 					.load( guiData.modelFileName, function ( group2 ) {
 					.load( guiData.modelFileName, function ( group2 ) {