LDrawLoader.js 45.2 KB
Newer Older
M
Marco Fugaro 已提交
1
( function () {
Y
yomboprime 已提交
2

3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
	// Note: "MATERIAL" tag (e.g. GLITTER, SPECKLE) is not implemented

	const FINISH_TYPE_DEFAULT = 0;
	const FINISH_TYPE_CHROME = 1;
	const FINISH_TYPE_PEARLESCENT = 2;
	const FINISH_TYPE_RUBBER = 3;
	const FINISH_TYPE_MATTE_METALLIC = 4;
	const FINISH_TYPE_METAL = 5; // State machine to search a subobject path.
	// The LDraw standard establishes these various possible subfolders.

	const FILE_LOCATION_AS_IS = 0;
	const FILE_LOCATION_TRY_PARTS = 1;
	const FILE_LOCATION_TRY_P = 2;
	const FILE_LOCATION_TRY_MODELS = 3;
	const FILE_LOCATION_TRY_RELATIVE = 4;
	const FILE_LOCATION_TRY_ABSOLUTE = 5;
	const FILE_LOCATION_NOT_FOUND = 6;
G
Garrett Johnson 已提交
20

21
	const _tempVec0 = new THREE.Vector3();
G
Garrett Johnson 已提交
22

23
	const _tempVec1 = new THREE.Vector3();
G
Garrett Johnson 已提交
24

M
Mr.doob 已提交
25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
	class LDrawConditionalLineMaterial extends THREE.ShaderMaterial {

		constructor( parameters ) {

			super( {
				uniforms: THREE.UniformsUtils.merge( [ THREE.UniformsLib.fog, {
					diffuse: {
						value: new THREE.Color()
					},
					opacity: {
						value: 1.0
					}
				} ] ),
				vertexShader:
      /* glsl */
      `
				attribute vec3 control0;
				attribute vec3 control1;
				attribute vec3 direction;
				varying float discardFlag;

				#include <common>
				#include <color_pars_vertex>
				#include <fog_pars_vertex>
				#include <logdepthbuf_pars_vertex>
				#include <clipping_planes_pars_vertex>
				void main() {
					#include <color_vertex>

					vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
					gl_Position = projectionMatrix * mvPosition;

					// Transform the line segment ends and control points into camera clip space
					vec4 c0 = projectionMatrix * modelViewMatrix * vec4( control0, 1.0 );
					vec4 c1 = projectionMatrix * modelViewMatrix * vec4( control1, 1.0 );
					vec4 p0 = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
					vec4 p1 = projectionMatrix * modelViewMatrix * vec4( position + direction, 1.0 );

					c0.xy /= c0.w;
					c1.xy /= c1.w;
					p0.xy /= p0.w;
					p1.xy /= p1.w;

					// Get the direction of the segment and an orthogonal vector
					vec2 dir = p1.xy - p0.xy;
					vec2 norm = vec2( -dir.y, dir.x );

					// Get control point directions from the line
					vec2 c0dir = c0.xy - p1.xy;
					vec2 c1dir = c1.xy - p1.xy;

					// If the vectors to the controls points are pointed in different directions away
					// from the line segment then the line should not be drawn.
					float d0 = dot( normalize( norm ), normalize( c0dir ) );
					float d1 = dot( normalize( norm ), normalize( c1dir ) );
					discardFlag = float( sign( d0 ) != sign( d1 ) );

					#include <logdepthbuf_vertex>
					#include <clipping_planes_vertex>
					#include <fog_vertex>
				}
			`,
				fragmentShader:
      /* glsl */
      `
			uniform vec3 diffuse;
			uniform float opacity;
			varying float discardFlag;

			#include <common>
			#include <color_pars_fragment>
			#include <fog_pars_fragment>
			#include <logdepthbuf_pars_fragment>
			#include <clipping_planes_pars_fragment>
			void main() {

				if ( discardFlag > 0.5 ) discard;

				#include <clipping_planes_fragment>
				vec3 outgoingLight = vec3( 0.0 );
				vec4 diffuseColor = vec4( diffuse, opacity );
				#include <logdepthbuf_fragment>
				#include <color_fragment>
				outgoingLight = diffuseColor.rgb; // simple shader
				gl_FragColor = vec4( outgoingLight, diffuseColor.a );
				#include <tonemapping_fragment>
				#include <encodings_fragment>
				#include <fog_fragment>
				#include <premultiplied_alpha_fragment>
			}
			`
			} );
			Object.defineProperties( this, {
				opacity: {
					get: function () {

						return this.uniforms.opacity.value;

					},
					set: function ( value ) {

						this.uniforms.opacity.value = value;

					}
				},
				color: {
					get: function () {

						return this.uniforms.diffuse.value;

					}
				}
			} );
			this.setValues( parameters );

		}

	}

144
	function smoothNormals( triangles, lineSegments ) {
G
Garrett Johnson 已提交
145

146
		function hashVertex( v ) {
G
Garrett Johnson 已提交
147

148 149 150 151 152 153 154
			// NOTE: 1e2 is pretty coarse but was chosen because it allows edges
			// to be smoothed as expected (see minifig arms). The errors between edges
			// could be due to matrix multiplication.
			const x = ~ ~ ( v.x * 1e2 );
			const y = ~ ~ ( v.y * 1e2 );
			const z = ~ ~ ( v.z * 1e2 );
			return `${x},${y},${z}`;
G
Garrett Johnson 已提交
155

156
		}
157

158
		function hashEdge( v0, v1 ) {
G
Garrett Johnson 已提交
159

160
			return `${hashVertex( v0 )}_${hashVertex( v1 )}`;
G
Garrett Johnson 已提交
161

162
		}
G
Garrett Johnson 已提交
163

164 165 166 167
		const hardEdges = new Set();
		const halfEdgeList = {};
		const fullHalfEdgeList = {};
		const normals = []; // Save the list of hard edges by hash
G
Garrett Johnson 已提交
168

169
		for ( let i = 0, l = lineSegments.length; i < l; i ++ ) {
G
Garrett Johnson 已提交
170

171 172 173 174 175
			const ls = lineSegments[ i ];
			const v0 = ls.v0;
			const v1 = ls.v1;
			hardEdges.add( hashEdge( v0, v1 ) );
			hardEdges.add( hashEdge( v1, v0 ) );
G
Garrett Johnson 已提交
176

177
		} // track the half edges associated with each triangle
G
Garrett Johnson 已提交
178 179


180
		for ( let i = 0, l = triangles.length; i < l; i ++ ) {
G
Garrett Johnson 已提交
181

182
			const tri = triangles[ i ];
G
Garrett Johnson 已提交
183

184
			for ( let i2 = 0, l2 = 3; i2 < l2; i2 ++ ) {
G
Garrett Johnson 已提交
185

186 187 188 189 190
				const index = i2;
				const next = ( i2 + 1 ) % 3;
				const v0 = tri[ `v${index}` ];
				const v1 = tri[ `v${next}` ];
				const hash = hashEdge( v0, v1 ); // don't add the triangle if the edge is supposed to be hard
G
Garrett Johnson 已提交
191

192 193 194
				if ( hardEdges.has( hash ) ) continue;
				halfEdgeList[ hash ] = tri;
				fullHalfEdgeList[ hash ] = tri;
G
Garrett Johnson 已提交
195

196
			}
G
Garrett Johnson 已提交
197

M
Mr.doob 已提交
198
		} // Iterate until we've tried to connect all triangles to share normals
G
Garrett Johnson 已提交
199

200

201
		while ( true ) {
202

203 204 205
			// Stop if there are no more triangles left
			const halfEdges = Object.keys( halfEdgeList );
			if ( halfEdges.length === 0 ) break; // Exhaustively find all connected triangles
206

207 208
			let i = 0;
			const queue = [ fullHalfEdgeList[ halfEdges[ 0 ] ] ];
209

210
			while ( i < queue.length ) {
211

212 213 214 215
				// initialize all vertex normals in this triangle
				const tri = queue[ i ];
				i ++;
				const faceNormal = tri.faceNormal;
G
Garrett Johnson 已提交
216

217
				if ( tri.n0 === null ) {
G
Garrett Johnson 已提交
218

M
Mr.doob 已提交
219
					tri.n0 = faceNormal.clone().multiplyScalar( tri.fromQuad ? 0.5 : 1.0 );
220
					normals.push( tri.n0 );
G
Garrett Johnson 已提交
221

222
				}
G
Garrett Johnson 已提交
223

224
				if ( tri.n1 === null ) {
G
Garrett Johnson 已提交
225

M
Mr.doob 已提交
226
					tri.n1 = faceNormal.clone().multiplyScalar( tri.fromQuad ? 0.5 : 1.0 );
227
					normals.push( tri.n1 );
G
Garrett Johnson 已提交
228

229
				}
G
Garrett Johnson 已提交
230

231
				if ( tri.n2 === null ) {
G
Garrett Johnson 已提交
232

233 234
					tri.n2 = faceNormal.clone();
					normals.push( tri.n2 );
G
Garrett Johnson 已提交
235

236
				} // Check if any edge is connected to another triangle edge
G
Garrett Johnson 已提交
237 238


239
				for ( let i2 = 0, l2 = 3; i2 < l2; i2 ++ ) {
G
Garrett Johnson 已提交
240

241 242 243 244
					const index = i2;
					const next = ( i2 + 1 ) % 3;
					const v0 = tri[ `v${index}` ];
					const v1 = tri[ `v${next}` ]; // delete this triangle from the list so it won't be found again
245

246 247 248 249
					const hash = hashEdge( v0, v1 );
					delete halfEdgeList[ hash ];
					const reverseHash = hashEdge( v1, v0 );
					const otherTri = fullHalfEdgeList[ reverseHash ];
250

251
					if ( otherTri ) {
252

253 254 255 256
						// NOTE: If the angle between triangles is > 67.5 degrees then assume it's
						// hard edge. There are some cases where the line segments do not line up exactly
						// with or span multiple triangle edges (see Lunar Vehicle wheels).
						if ( Math.abs( otherTri.faceNormal.dot( tri.faceNormal ) ) < 0.25 ) {
257

258
							continue;
259

260 261 262
						} // 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.
Y
yomboprime 已提交
263 264


265
						if ( reverseHash in halfEdgeList ) {
Y
yomboprime 已提交
266

267 268
							queue.push( otherTri );
							delete halfEdgeList[ reverseHash ];
Y
yomboprime 已提交
269

270
						} // Find the matching edge in this triangle and copy the normal vector over
Y
yomboprime 已提交
271

272

273
						for ( let i3 = 0, l3 = 3; i3 < l3; i3 ++ ) {
Y
yomboprime 已提交
274

275 276 277 278 279
							const otherIndex = i3;
							const otherNext = ( i3 + 1 ) % 3;
							const otherV0 = otherTri[ `v${otherIndex}` ];
							const otherV1 = otherTri[ `v${otherNext}` ];
							const otherHash = hashEdge( otherV0, otherV1 );
Y
yomboprime 已提交
280

281
							if ( otherHash === reverseHash ) {
Y
yomboprime 已提交
282

283
								if ( otherTri[ `n${otherIndex}` ] === null ) {
Y
yomboprime 已提交
284

285 286
									const norm = tri[ `n${next}` ];
									otherTri[ `n${otherIndex}` ] = norm;
M
Mr.doob 已提交
287 288
									const isDoubledVert = otherTri.fromQuad && otherIndex !== 2;
									norm.addScaledVector( otherTri.faceNormal, isDoubledVert ? 0.5 : 1.0 );
Y
yomboprime 已提交
289

290
								}
Y
yomboprime 已提交
291

292
								if ( otherTri[ `n${otherNext}` ] === null ) {
Y
yomboprime 已提交
293

294 295
									const norm = tri[ `n${index}` ];
									otherTri[ `n${otherNext}` ] = norm;
M
Mr.doob 已提交
296 297
									const isDoubledVert = otherTri.fromQuad && otherNext !== 2;
									norm.addScaledVector( otherTri.faceNormal, isDoubledVert ? 0.5 : 1.0 );
Y
yomboprime 已提交
298

M
Marco Fugaro 已提交
299
								}
Y
yomboprime 已提交
300

301 302
								break;

M
Marco Fugaro 已提交
303
							}
Y
yomboprime 已提交
304

M
Marco Fugaro 已提交
305
						}
Y
yomboprime 已提交
306

M
Marco Fugaro 已提交
307
					}
Y
yomboprime 已提交
308 309 310

				}

311
			}
Y
yomboprime 已提交
312

313
		} // The normals of each face have been added up so now we average them by normalizing the vector.
Y
yomboprime 已提交
314 315


316
		for ( let i = 0, l = normals.length; i < l; i ++ ) {
Y
yomboprime 已提交
317

318
			normals[ i ].normalize();
Y
yomboprime 已提交
319 320 321

		}

322
	}
323

324
	function isPrimitiveType( type ) {
M
Mugen87 已提交
325

326 327 328
		return /primitive/i.test( type ) || type === 'Subpart';

	}
M
Mugen87 已提交
329

330 331 332
	class LineParser {

		constructor( line, lineNumber ) {
M
Mugen87 已提交
333

M
Marco Fugaro 已提交
334 335 336 337 338
			this.line = line;
			this.lineLength = line.length;
			this.currentCharIndex = 0;
			this.currentChar = ' ';
			this.lineNumber = lineNumber;
M
Mugen87 已提交
339

Y
yomboprime 已提交
340
		}
M
Mugen87 已提交
341

342
		seekNonSpace() {
Y
yomboprime 已提交
343

344
			while ( this.currentCharIndex < this.lineLength ) {
Y
yomboprime 已提交
345

346
				this.currentChar = this.line.charAt( this.currentCharIndex );
Y
yomboprime 已提交
347

348
				if ( this.currentChar !== ' ' && this.currentChar !== '\t' ) {
Y
yomboprime 已提交
349

350
					return;
Y
yomboprime 已提交
351

M
Marco Fugaro 已提交
352
				}
Y
yomboprime 已提交
353

354
				this.currentCharIndex ++;
Y
yomboprime 已提交
355

356
			}
G
Garrett Johnson 已提交
357

358
		}
Y
yomboprime 已提交
359

360
		getToken() {
Y
yomboprime 已提交
361

362
			const pos0 = this.currentCharIndex ++; // Seek space
Y
yomboprime 已提交
363

364
			while ( this.currentCharIndex < this.lineLength ) {
Y
yomboprime 已提交
365

366 367 368
				this.currentChar = this.line.charAt( this.currentCharIndex );

				if ( this.currentChar === ' ' || this.currentChar === '\t' ) {
M
Marco Fugaro 已提交
369

370
					break;
Y
yomboprime 已提交
371 372 373

				}

374
				this.currentCharIndex ++;
Y
yomboprime 已提交
375

376
			}
Y
yomboprime 已提交
377

378 379 380
			const pos1 = this.currentCharIndex;
			this.seekNonSpace();
			return this.line.substring( pos0, pos1 );
Y
yomboprime 已提交
381

382
		}
Y
yomboprime 已提交
383

384
		getRemainingString() {
Y
yomboprime 已提交
385

386
			return this.line.substring( this.currentCharIndex, this.lineLength );
M
Mugen87 已提交
387

388
		}
Y
yomboprime 已提交
389

390
		isAtTheEnd() {
Y
yomboprime 已提交
391

392
			return this.currentCharIndex >= this.lineLength;
Y
yomboprime 已提交
393

394 395 396
		}

		setToEnd() {
G
Garrett Johnson 已提交
397

398
			this.currentCharIndex = this.lineLength;
G
Garrett Johnson 已提交
399

400
		}
G
Garrett Johnson 已提交
401

402
		getLineNumberString() {
Y
yomboprime 已提交
403

404
			return this.lineNumber >= 0 ? ' at line ' + this.lineNumber : '';
M
Mugen87 已提交
405

406
		}
Y
yomboprime 已提交
407

408
	}
Y
yomboprime 已提交
409

410
	function sortByMaterial( a, b ) {
Y
yomboprime 已提交
411

412 413 414
		if ( a.colourCode === b.colourCode ) {

			return 0;
Y
yomboprime 已提交
415 416 417

		}

418
		if ( a.colourCode < b.colourCode ) {
419

420
			return - 1;
421

422
		}
423

424
		return 1;
Y
yomboprime 已提交
425

426
	}
Y
yomboprime 已提交
427

428
	function createObject( elements, elementSize, isConditionalSegments ) {
Y
yomboprime 已提交
429

430 431 432 433 434 435 436 437 438 439 440
		// Creates a THREE.LineSegments (elementSize = 2) or a THREE.Mesh (elementSize = 3 )
		// With per face / segment material, implemented with mesh groups and materials array
		// Sort the triangles or line segments by colour code to make later the mesh groups
		elements.sort( sortByMaterial );
		const positions = [];
		const normals = [];
		const materials = [];
		const bufferGeometry = new THREE.BufferGeometry();
		let prevMaterial = null;
		let index0 = 0;
		let numGroupVerts = 0;
Y
yomboprime 已提交
441

442 443 444 445 446
		for ( let iElem = 0, nElem = elements.length; iElem < nElem; iElem ++ ) {

			const elem = elements[ iElem ];
			const v0 = elem.v0;
			const v1 = elem.v1; // Note that LDraw coordinate system is rotated 180 deg. in the X axis w.r.t. Three.js's one
Y
yomboprime 已提交
447

448
			positions.push( v0.x, v0.y, v0.z, v1.x, v1.y, v1.z );
Y
yomboprime 已提交
449

450
			if ( elementSize === 3 ) {
Y
yomboprime 已提交
451

452 453 454 455 456 457 458
				positions.push( elem.v2.x, elem.v2.y, elem.v2.z );
				const n0 = elem.n0 || elem.faceNormal;
				const n1 = elem.n1 || elem.faceNormal;
				const 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 );
Y
yomboprime 已提交
459

460
			}
Y
yomboprime 已提交
461

462
			if ( prevMaterial !== elem.material ) {
Y
yomboprime 已提交
463

464
				if ( prevMaterial !== null ) {
465

466
					bufferGeometry.addGroup( index0, numGroupVerts, materials.length - 1 );
467

M
Marco Fugaro 已提交
468
				}
Y
yomboprime 已提交
469

470 471 472 473
				materials.push( elem.material );
				prevMaterial = elem.material;
				index0 = iElem * elementSize;
				numGroupVerts = elementSize;
Y
yomboprime 已提交
474

475
			} else {
Y
yomboprime 已提交
476

477
				numGroupVerts += elementSize;
Y
yomboprime 已提交
478

M
Marco Fugaro 已提交
479
			}
Y
yomboprime 已提交
480

481
		}
Y
yomboprime 已提交
482

483
		if ( numGroupVerts > 0 ) {
484

485
			bufferGeometry.addGroup( index0, Infinity, materials.length - 1 );
486

487
		}
Y
yomboprime 已提交
488

489
		bufferGeometry.setAttribute( 'position', new THREE.Float32BufferAttribute( positions, 3 ) );
Y
yomboprime 已提交
490

491
		if ( elementSize === 3 ) {
Y
yomboprime 已提交
492

493
			bufferGeometry.setAttribute( 'normal', new THREE.Float32BufferAttribute( normals, 3 ) );
Y
yomboprime 已提交
494

495
		}
Y
yomboprime 已提交
496

497
		let object3d = null;
Y
yomboprime 已提交
498

499
		if ( elementSize === 2 ) {
Y
yomboprime 已提交
500

501
			object3d = new THREE.LineSegments( bufferGeometry, materials );
Y
yomboprime 已提交
502

503 504 505 506 507
		} else if ( elementSize === 3 ) {

			object3d = new THREE.Mesh( bufferGeometry, materials );

		}
Y
yomboprime 已提交
508

509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541
		if ( isConditionalSegments ) {

			object3d.isConditionalLine = true;
			const controlArray0 = new Float32Array( elements.length * 3 * 2 );
			const controlArray1 = new Float32Array( elements.length * 3 * 2 );
			const directionArray = new Float32Array( elements.length * 3 * 2 );

			for ( let i = 0, l = elements.length; i < l; i ++ ) {

				const os = elements[ i ];
				const c0 = os.c0;
				const c1 = os.c1;
				const v0 = os.v0;
				const v1 = os.v1;
				const index = i * 3 * 2;
				controlArray0[ index + 0 ] = c0.x;
				controlArray0[ index + 1 ] = c0.y;
				controlArray0[ index + 2 ] = c0.z;
				controlArray0[ index + 3 ] = c0.x;
				controlArray0[ index + 4 ] = c0.y;
				controlArray0[ index + 5 ] = c0.z;
				controlArray1[ index + 0 ] = c1.x;
				controlArray1[ index + 1 ] = c1.y;
				controlArray1[ index + 2 ] = c1.z;
				controlArray1[ index + 3 ] = c1.x;
				controlArray1[ index + 4 ] = c1.y;
				controlArray1[ index + 5 ] = c1.z;
				directionArray[ index + 0 ] = v1.x - v0.x;
				directionArray[ index + 1 ] = v1.y - v0.y;
				directionArray[ index + 2 ] = v1.z - v0.z;
				directionArray[ index + 3 ] = v1.x - v0.x;
				directionArray[ index + 4 ] = v1.y - v0.y;
				directionArray[ index + 5 ] = v1.z - v0.z;
Y
yomboprime 已提交
542

M
Marco Fugaro 已提交
543
			}
Y
yomboprime 已提交
544

545 546 547 548 549 550 551 552 553
			bufferGeometry.setAttribute( 'control0', new THREE.BufferAttribute( controlArray0, 3, false ) );
			bufferGeometry.setAttribute( 'control1', new THREE.BufferAttribute( controlArray1, 3, false ) );
			bufferGeometry.setAttribute( 'direction', new THREE.BufferAttribute( directionArray, 3, false ) );

		}

		return object3d;

	} //
Y
yomboprime 已提交
554 555


556
	class LDrawLoader extends THREE.Loader {
Y
yomboprime 已提交
557

558
		constructor( manager ) {
559

560
			super( manager ); // This is a stack of 'parse scopes' with one level per subobject loaded file.
M
Marco Fugaro 已提交
561 562 563
			// Each level contains a material lib and also other runtime variables passed between parent and child subobjects
			// When searching for a material code, the stack is read from top of the stack to bottom
			// Each material library is an object map keyed by colour codes.
Y
yomboprime 已提交
564

M
Marco Fugaro 已提交
565
			this.parseScopesStack = null; // Array of THREE.Material
Y
yomboprime 已提交
566

M
Marco Fugaro 已提交
567 568
			this.materials = []; // Not using THREE.Cache here because it returns the previous HTML error response instead of calling onError()
			// This also allows to handle the embedded text files ("0 FILE" lines)
Y
yomboprime 已提交
569

M
Marco Fugaro 已提交
570
			this.subobjectCache = {}; // This object is a map from file names to paths. It agilizes the paths search. If it is not set then files will be searched by trial and error.
Y
yomboprime 已提交
571

M
Marco Fugaro 已提交
572
			this.fileMap = null; // Add default main triangle and line edge materials (used in piecess that can be coloured with a main color)
Y
yomboprime 已提交
573

M
Marco Fugaro 已提交
574 575
			this.setMaterials( [ this.parseColourMetaDirective( new LineParser( 'Main_Colour CODE 16 VALUE #FF8080 EDGE #333333' ) ), this.parseColourMetaDirective( new LineParser( 'Edge_Colour CODE 24 VALUE #A0A0A0 EDGE #333333' ) ) ] ); // If this flag is set to true, each subobject will be a Object.
			// If not (the default), only one object which contains all the merged primitives will be created.
Y
yomboprime 已提交
576

M
Marco Fugaro 已提交
577
			this.separateObjects = false; // If this flag is set to true the vertex normals will be smoothed.
Y
yomboprime 已提交
578

M
Marco Fugaro 已提交
579
			this.smoothNormals = true;
580

581
		}
Y
yomboprime 已提交
582

583
		load( url, onLoad, onProgress, onError ) {
Y
yomboprime 已提交
584

585
			if ( ! this.fileMap ) {
586

587
				this.fileMap = {};
Y
yomboprime 已提交
588

589
			}
Y
yomboprime 已提交
590

591 592 593 594 595 596
			const scope = this;
			const fileLoader = new THREE.FileLoader( this.manager );
			fileLoader.setPath( this.path );
			fileLoader.setRequestHeader( this.requestHeader );
			fileLoader.setWithCredentials( this.withCredentials );
			fileLoader.load( url, function ( text ) {
Y
yomboprime 已提交
597

598
				scope.processObject( text, onLoad, null, url );
Y
yomboprime 已提交
599

600
			}, onProgress, onError );
Y
yomboprime 已提交
601

602
		}
Y
yomboprime 已提交
603

604
		parse( text, path, onLoad ) {
Y
yomboprime 已提交
605

M
Michael Herzog 已提交
606
			// Async parse.  This function calls onParse with the parsed THREE.Object3D as parameter
607
			this.processObject( text, onLoad, null, path );
Y
yomboprime 已提交
608

609
		}
610

611
		setMaterials( materials ) {
Y
yomboprime 已提交
612

613 614 615 616 617 618
			// Clears parse scopes stack, adds new scope with material library
			this.parseScopesStack = [];
			this.newParseScopeLevel( materials );
			this.getCurrentParseScope().isFromParse = false;
			this.materials = materials;
			return this;
Y
yomboprime 已提交
619

620
		}
Y
yomboprime 已提交
621

622
		setFileMap( fileMap ) {
Y
yomboprime 已提交
623

624 625
			this.fileMap = fileMap;
			return this;
Y
yomboprime 已提交
626

627
		}
Y
yomboprime 已提交
628

629
		newParseScopeLevel( materials ) {
Y
yomboprime 已提交
630

631 632
			// Adds a new scope level, assign materials to it and returns it
			const matLib = {};
Y
yomboprime 已提交
633

634
			if ( materials ) {
Y
yomboprime 已提交
635

636
				for ( let i = 0, n = materials.length; i < n; i ++ ) {
Y
yomboprime 已提交
637

638 639
					const material = materials[ i ];
					matLib[ material.userData.code ] = material;
Y
yomboprime 已提交
640

M
Marco Fugaro 已提交
641
				}
Y
yomboprime 已提交
642

643
			}
Y
yomboprime 已提交
644

645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671
			const topParseScope = this.getCurrentParseScope();
			const newParseScope = {
				lib: matLib,
				url: null,
				// Subobjects
				subobjects: null,
				numSubobjects: 0,
				subobjectIndex: 0,
				inverted: false,
				category: null,
				keywords: null,
				// Current subobject
				currentFileName: null,
				mainColourCode: topParseScope ? topParseScope.mainColourCode : '16',
				mainEdgeColourCode: topParseScope ? topParseScope.mainEdgeColourCode : '24',
				currentMatrix: new THREE.Matrix4(),
				matrix: new THREE.Matrix4(),
				// If false, it is a root material scope previous to parse
				isFromParse: true,
				triangles: null,
				lineSegments: null,
				conditionalSegments: null,
				// If true, this object is the start of a construction step
				startingConstructionStep: false
			};
			this.parseScopesStack.push( newParseScope );
			return newParseScope;
Y
yomboprime 已提交
672

673
		}
Y
yomboprime 已提交
674

675
		removeScopeLevel() {
Y
yomboprime 已提交
676

677 678
			this.parseScopesStack.pop();
			return this;
Y
yomboprime 已提交
679

680
		}
Y
yomboprime 已提交
681

682
		addMaterial( material ) {
Y
yomboprime 已提交
683

684 685
			// Adds a material to the material library which is on top of the parse scopes stack. And also to the materials array
			const matLib = this.getCurrentParseScope().lib;
Y
yomboprime 已提交
686

687
			if ( ! matLib[ material.userData.code ] ) {
Y
yomboprime 已提交
688

689
				this.materials.push( material );
Y
yomboprime 已提交
690

691
			}
Y
yomboprime 已提交
692

693 694
			matLib[ material.userData.code ] = material;
			return this;
Y
yomboprime 已提交
695

696
		}
Y
yomboprime 已提交
697

698
		getMaterial( colourCode ) {
Y
yomboprime 已提交
699

700 701
			// Given a colour code search its material in the parse scopes stack
			if ( colourCode.startsWith( '0x2' ) ) {
Y
yomboprime 已提交
702

703 704 705
				// Special 'direct' material value (RGB colour)
				const colour = colourCode.substring( 3 );
				return this.parseColourMetaDirective( new LineParser( 'Direct_Color_' + colour + ' CODE -1 VALUE #' + colour + ' EDGE #' + colour + '' ) );
Y
yomboprime 已提交
706

707
			}
Y
yomboprime 已提交
708

709
			for ( let i = this.parseScopesStack.length - 1; i >= 0; i -- ) {
Y
yomboprime 已提交
710

711
				const material = this.parseScopesStack[ i ].lib[ colourCode ];
Y
yomboprime 已提交
712

713
				if ( material ) {
Y
yomboprime 已提交
714

715
					return material;
Y
yomboprime 已提交
716

M
Marco Fugaro 已提交
717
				}
Y
yomboprime 已提交
718

719
			} // Material was not found
Y
yomboprime 已提交
720 721


722
			return null;
Y
yomboprime 已提交
723

724
		}
Y
yomboprime 已提交
725

726
		getParentParseScope() {
Y
yomboprime 已提交
727

728
			if ( this.parseScopesStack.length > 1 ) {
Y
yomboprime 已提交
729

730
				return this.parseScopesStack[ this.parseScopesStack.length - 2 ];
Y
yomboprime 已提交
731

732
			}
Y
yomboprime 已提交
733

734
			return null;
Y
yomboprime 已提交
735

736
		}
Y
yomboprime 已提交
737

738
		getCurrentParseScope() {
Y
yomboprime 已提交
739

740
			if ( this.parseScopesStack.length > 0 ) {
741

742
				return this.parseScopesStack[ this.parseScopesStack.length - 1 ];
Y
yomboprime 已提交
743

744
			}
Y
yomboprime 已提交
745

746
			return null;
Y
yomboprime 已提交
747

748
		}
Y
yomboprime 已提交
749

750
		parseColourMetaDirective( lineParser ) {
Y
yomboprime 已提交
751

752
			// Parses a colour definition and returns a THREE.Material or null if error
753
			let code = null; // Triangle and line colours
Y
yomboprime 已提交
754

755 756
			let colour = 0xFF00FF;
			let edgeColour = 0xFF00FF; // Transparency
Y
yomboprime 已提交
757

758 759
			let alpha = 1;
			let isTransparent = false; // Self-illumination:
Y
yomboprime 已提交
760

761 762 763 764
			let luminance = 0;
			let finishType = FINISH_TYPE_DEFAULT;
			let edgeMaterial = null;
			const name = lineParser.getToken();
Y
yomboprime 已提交
765

766
			if ( ! name ) {
Y
yomboprime 已提交
767

768
				throw 'LDrawLoader: Material name was expected after "!COLOUR tag' + lineParser.getLineNumberString() + '.';
Y
yomboprime 已提交
769

770
			} // Parse tag tokens and their parameters
Y
yomboprime 已提交
771 772


773
			let token = null;
Y
yomboprime 已提交
774

775
			while ( true ) {
Y
yomboprime 已提交
776

777
				token = lineParser.getToken();
Y
yomboprime 已提交
778

779
				if ( ! token ) {
Y
yomboprime 已提交
780

781
					break;
Y
yomboprime 已提交
782

783
				}
M
Mugen87 已提交
784

785
				switch ( token.toUpperCase() ) {
M
Mugen87 已提交
786

787 788 789
					case 'CODE':
						code = lineParser.getToken();
						break;
M
Mugen87 已提交
790

791 792
					case 'VALUE':
						colour = lineParser.getToken();
Y
yomboprime 已提交
793

794
						if ( colour.startsWith( '0x' ) ) {
Y
yomboprime 已提交
795

796
							colour = '#' + colour.substring( 2 );
Y
yomboprime 已提交
797

798
						} else if ( ! colour.startsWith( '#' ) ) {
Y
yomboprime 已提交
799

800
							throw 'LDrawLoader: Invalid colour while parsing material' + lineParser.getLineNumberString() + '.';
Y
yomboprime 已提交
801

802
						}
Y
yomboprime 已提交
803

804
						break;
Y
yomboprime 已提交
805

806 807
					case 'EDGE':
						edgeColour = lineParser.getToken();
Y
yomboprime 已提交
808

809
						if ( edgeColour.startsWith( '0x' ) ) {
810

811
							edgeColour = '#' + edgeColour.substring( 2 );
M
Mugen87 已提交
812

813
						} else if ( ! edgeColour.startsWith( '#' ) ) {
Y
yomboprime 已提交
814

815 816
							// Try to see if edge colour is a colour code
							edgeMaterial = this.getMaterial( edgeColour );
Y
yomboprime 已提交
817

818
							if ( ! edgeMaterial ) {
Y
yomboprime 已提交
819

820
								throw 'LDrawLoader: Invalid edge colour while parsing material' + lineParser.getLineNumberString() + '.';
Y
yomboprime 已提交
821

822
							} // Get the edge material for this triangle material
Y
yomboprime 已提交
823

M
Marco Fugaro 已提交
824

825
							edgeMaterial = edgeMaterial.userData.edgeMaterial;
Y
yomboprime 已提交
826

827
						}
Y
yomboprime 已提交
828

829
						break;
Y
yomboprime 已提交
830

831 832
					case 'ALPHA':
						alpha = parseInt( lineParser.getToken() );
Y
yomboprime 已提交
833

834
						if ( isNaN( alpha ) ) {
Y
yomboprime 已提交
835

836
							throw 'LDrawLoader: Invalid alpha value in material definition' + lineParser.getLineNumberString() + '.';
Y
yomboprime 已提交
837

838
						}
Y
yomboprime 已提交
839

840
						alpha = Math.max( 0, Math.min( 1, alpha / 255 ) );
Y
yomboprime 已提交
841

842
						if ( alpha < 1 ) {
Y
yomboprime 已提交
843

844
							isTransparent = true;
Y
yomboprime 已提交
845

846
						}
Y
yomboprime 已提交
847

848
						break;
Y
yomboprime 已提交
849

850 851
					case 'LUMINANCE':
						luminance = parseInt( lineParser.getToken() );
Y
yomboprime 已提交
852

853 854 855 856 857 858 859
						if ( isNaN( luminance ) ) {

							throw 'LDrawLoader: Invalid luminance value in material definition' + LineParser.getLineNumberString() + '.';

						}

						luminance = Math.max( 0, Math.min( 1, luminance / 255 ) );
Y
yomboprime 已提交
860 861
						break;

862 863
					case 'CHROME':
						finishType = FINISH_TYPE_CHROME;
Y
yomboprime 已提交
864 865
						break;

866 867
					case 'PEARLESCENT':
						finishType = FINISH_TYPE_PEARLESCENT;
Y
yomboprime 已提交
868 869
						break;

870 871
					case 'RUBBER':
						finishType = FINISH_TYPE_RUBBER;
Y
yomboprime 已提交
872 873
						break;

874 875
					case 'MATTE_METALLIC':
						finishType = FINISH_TYPE_MATTE_METALLIC;
Y
yomboprime 已提交
876 877
						break;

878 879 880 881 882
					case 'METAL':
						finishType = FINISH_TYPE_METAL;
						break;

					case 'MATERIAL':
M
Michael Herzog 已提交
883
						// Not implemented
884
						lineParser.setToEnd();
Y
yomboprime 已提交
885 886 887
						break;

					default:
888
						throw 'LDrawLoader: Unknown token "' + token + '" while parsing material' + lineParser.getLineNumberString() + '.';
Y
yomboprime 已提交
889 890 891 892
						break;

				}

893
			}
Y
yomboprime 已提交
894

895
			let material = null;
Y
yomboprime 已提交
896

897
			switch ( finishType ) {
M
Mugen87 已提交
898

899 900 901 902 903 904
				case FINISH_TYPE_DEFAULT:
					material = new THREE.MeshStandardMaterial( {
						color: colour,
						roughness: 0.3,
						envMapIntensity: 0.3,
						metalness: 0
M
Marco Fugaro 已提交
905
					} );
906 907 908
					break;

				case FINISH_TYPE_PEARLESCENT:
M
Michael Herzog 已提交
909
					// Try to imitate pearlescency by setting the specular to the complementary of the color, and low shininess
910 911
					const specular = new THREE.Color( colour );
					const hsl = specular.getHSL( {
912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927
						h: 0,
						s: 0,
						l: 0
					} );
					hsl.h = ( hsl.h + 0.5 ) % 1;
					hsl.l = Math.min( 1, hsl.l + ( 1 - hsl.l ) * 0.7 );
					specular.setHSL( hsl.h, hsl.s, hsl.l );
					material = new THREE.MeshPhongMaterial( {
						color: colour,
						specular: specular,
						shininess: 10,
						reflectivity: 0.3
					} );
					break;

				case FINISH_TYPE_CHROME:
M
Michael Herzog 已提交
928
					// Mirror finish surface
929 930 931 932 933 934 935 936
					material = new THREE.MeshStandardMaterial( {
						color: colour,
						roughness: 0,
						metalness: 1
					} );
					break;

				case FINISH_TYPE_RUBBER:
M
Michael Herzog 已提交
937
					// Rubber finish
938 939 940 941 942 943 944 945
					material = new THREE.MeshStandardMaterial( {
						color: colour,
						roughness: 0.9,
						metalness: 0
					} );
					break;

				case FINISH_TYPE_MATTE_METALLIC:
M
Michael Herzog 已提交
946
					// Brushed metal finish
947 948 949 950 951 952 953 954
					material = new THREE.MeshStandardMaterial( {
						color: colour,
						roughness: 0.8,
						metalness: 0.4
					} );
					break;

				case FINISH_TYPE_METAL:
M
Michael Herzog 已提交
955
					// Average metal finish
956 957 958 959
					material = new THREE.MeshStandardMaterial( {
						color: colour,
						roughness: 0.2,
						metalness: 0.85
M
Marco Fugaro 已提交
960
					} );
961
					break;
Y
yomboprime 已提交
962

963
				default:
M
Michael Herzog 已提交
964
					// Should not happen
965
					break;
966

967
			}
968

969 970 971 972 973 974
			material.transparent = isTransparent;
			material.premultipliedAlpha = true;
			material.opacity = alpha;
			material.depthWrite = ! isTransparent;
			material.polygonOffset = true;
			material.polygonOffsetFactor = 1;
Y
yomboprime 已提交
975

976
			if ( luminance !== 0 ) {
Y
yomboprime 已提交
977

978
				material.emissive.set( material.color ).multiplyScalar( luminance );
Y
yomboprime 已提交
979

980
			}
Y
yomboprime 已提交
981

982
			if ( ! edgeMaterial ) {
Y
yomboprime 已提交
983

984 985 986 987 988 989 990 991
				// This is the material used for edges
				edgeMaterial = new THREE.LineBasicMaterial( {
					color: edgeColour,
					transparent: isTransparent,
					opacity: alpha,
					depthWrite: ! isTransparent
				} );
				edgeMaterial.userData.code = code;
M
Mr.doob 已提交
992 993 994
				edgeMaterial.name = name + ' - Edge'; // This is the material used for conditional edges

				edgeMaterial.userData.conditionalEdgeMaterial = new LDrawConditionalLineMaterial( {
995 996
					fog: true,
					transparent: isTransparent,
M
Mr.doob 已提交
997 998 999
					depthWrite: ! isTransparent,
					color: edgeColour,
					opacity: alpha
1000
				} );
Y
yomboprime 已提交
1001

1002
			}
Y
yomboprime 已提交
1003

1004 1005 1006 1007
			material.userData.code = code;
			material.name = name;
			material.userData.edgeMaterial = edgeMaterial;
			return material;
1008

1009
		} //
Y
yomboprime 已提交
1010

Y
yomboprime 已提交
1011

1012
		objectParse( text ) {
Y
yomboprime 已提交
1013

1014
			// Retrieve data from the parent parse scope
1015
			const parentParseScope = this.getParentParseScope(); // Main colour codes passed to this subobject (or default codes 16 and 24 if it is the root object)
Y
yomboprime 已提交
1016

1017 1018 1019
			const mainColourCode = parentParseScope.mainColourCode;
			const mainEdgeColourCode = parentParseScope.mainEdgeColourCode;
			const currentParseScope = this.getCurrentParseScope(); // Parse result variables
Y
yomboprime 已提交
1020

1021 1022 1023 1024 1025 1026
			let triangles;
			let lineSegments;
			let conditionalSegments;
			const subobjects = [];
			let category = null;
			let keywords = null;
Y
yomboprime 已提交
1027

1028
			if ( text.indexOf( '\r\n' ) !== - 1 ) {
M
Mugen87 已提交
1029

1030 1031
				// This is faster than String.split with regex that splits on both
				text = text.replace( /\r\n/g, '\n' );
Y
yomboprime 已提交
1032

1033
			}
Y
yomboprime 已提交
1034

1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046
			const lines = text.split( '\n' );
			const numLines = lines.length;
			let parsingEmbeddedFiles = false;
			let currentEmbeddedFileName = null;
			let currentEmbeddedText = null;
			let bfcCertified = false;
			let bfcCCW = true;
			let bfcInverted = false;
			let bfcCull = true;
			let type = '';
			let startingConstructionStep = false;
			const scope = this;
Y
yomboprime 已提交
1047

1048
			function parseColourCode( lineParser, forEdge ) {
Y
yomboprime 已提交
1049

1050
				// Parses next colour code and returns a THREE.Material
1051
				let colourCode = lineParser.getToken();
Y
yomboprime 已提交
1052

1053 1054 1055
				if ( ! forEdge && colourCode === '16' ) {

					colourCode = mainColourCode;
Y
yomboprime 已提交
1056 1057 1058

				}

1059 1060 1061
				if ( forEdge && colourCode === '24' ) {

					colourCode = mainEdgeColourCode;
Y
yomboprime 已提交
1062

1063
				}
Y
yomboprime 已提交
1064

1065
				const material = scope.getMaterial( colourCode );
1066

1067
				if ( ! material ) {
1068

1069
					throw 'LDrawLoader: Unknown colour code "' + colourCode + '" is used' + lineParser.getLineNumberString() + ' but it was not defined previously.';
1070

1071
				}
1072

1073
				return material;
1074

1075
			}
1076

1077
			function parseVector( lp ) {
1078

1079
				const v = new THREE.Vector3( parseFloat( lp.getToken() ), parseFloat( lp.getToken() ), parseFloat( lp.getToken() ) );
Y
yomboprime 已提交
1080

1081
				if ( ! scope.separateObjects ) {
Y
yomboprime 已提交
1082

1083
					v.applyMatrix4( currentParseScope.currentMatrix );
Y
yomboprime 已提交
1084

1085
				}
Y
yomboprime 已提交
1086

1087
				return v;
Y
yomboprime 已提交
1088

1089
			} // Parse all line commands
Y
yomboprime 已提交
1090 1091


1092
			for ( let lineIndex = 0; lineIndex < numLines; lineIndex ++ ) {
Y
yomboprime 已提交
1093

1094
				const line = lines[ lineIndex ];
1095
				if ( line.length === 0 ) continue;
Y
yomboprime 已提交
1096

1097
				if ( parsingEmbeddedFiles ) {
Y
yomboprime 已提交
1098

1099
					if ( line.startsWith( '0 FILE ' ) ) {
Y
yomboprime 已提交
1100

1101 1102
						// Save previous embedded file in the cache
						this.subobjectCache[ currentEmbeddedFileName.toLowerCase() ] = currentEmbeddedText; // New embedded text file
Y
yomboprime 已提交
1103

1104 1105
						currentEmbeddedFileName = line.substring( 7 );
						currentEmbeddedText = '';
Y
yomboprime 已提交
1106

1107
					} else {
Y
yomboprime 已提交
1108

1109
						currentEmbeddedText += line + '\n';
M
Mugen87 已提交
1110

1111
					}
M
Mugen87 已提交
1112

1113
					continue;
Y
yomboprime 已提交
1114

1115
				}
Y
yomboprime 已提交
1116

1117
				const lp = new LineParser( line, lineIndex + 1 );
1118
				lp.seekNonSpace();
1119

1120
				if ( lp.isAtTheEnd() ) {
1121

1122 1123
					// Empty line
					continue;
1124

1125
				} // Parse the line type
1126 1127


1128 1129 1130 1131 1132 1133 1134
				const lineType = lp.getToken();
				let material;
				let segment;
				let inverted;
				let ccw;
				let doubleSided;
				let v0, v1, v2, v3, faceNormal;
Y
yomboprime 已提交
1135

1136
				switch ( lineType ) {
G
Garrett Johnson 已提交
1137

1138 1139
					// Line type 0: Comment or META
					case '0':
M
Michael Herzog 已提交
1140
						// Parse meta directive
1141
						const meta = lp.getToken();
1142

1143
						if ( meta ) {
Y
yomboprime 已提交
1144

1145
							switch ( meta ) {
Y
yomboprime 已提交
1146

1147 1148 1149 1150 1151 1152
								case '!LDRAW_ORG':
									type = lp.getToken();
									currentParseScope.triangles = [];
									currentParseScope.lineSegments = [];
									currentParseScope.conditionalSegments = [];
									currentParseScope.type = type;
1153
									const isRoot = ! parentParseScope.isFromParse;
Y
yomboprime 已提交
1154

1155
									if ( isRoot || scope.separateObjects && ! isPrimitiveType( type ) ) {
Y
yomboprime 已提交
1156

1157 1158
										currentParseScope.groupObject = new THREE.Group();
										currentParseScope.groupObject.userData.startingConstructionStep = currentParseScope.startingConstructionStep;
Y
yomboprime 已提交
1159

1160 1161
									} // If the scale of the object is negated then the triangle winding order
									// needs to be flipped.
Y
yomboprime 已提交
1162 1163


1164
									if ( currentParseScope.matrix.determinant() < 0 && ( scope.separateObjects && isPrimitiveType( type ) || ! scope.separateObjects ) ) {
Y
yomboprime 已提交
1165

1166
										currentParseScope.inverted = ! currentParseScope.inverted;
1167

1168
									}
Y
yomboprime 已提交
1169

1170 1171 1172 1173
									triangles = currentParseScope.triangles;
									lineSegments = currentParseScope.lineSegments;
									conditionalSegments = currentParseScope.conditionalSegments;
									break;
Y
yomboprime 已提交
1174

1175
								case '!COLOUR':
1176
									material = this.parseColourMetaDirective( lp );
Y
yomboprime 已提交
1177

1178
									if ( material ) {
M
Mugen87 已提交
1179

1180
										this.addMaterial( material );
Y
yomboprime 已提交
1181

1182
									} else {
Y
yomboprime 已提交
1183

1184
										console.warn( 'LDrawLoader: Error parsing material' + lp.getLineNumberString() );
Y
yomboprime 已提交
1185

1186
									}
Y
yomboprime 已提交
1187

1188
									break;
G
Garrett Johnson 已提交
1189

1190 1191 1192
								case '!CATEGORY':
									category = lp.getToken();
									break;
G
Garrett Johnson 已提交
1193

1194
								case '!KEYWORDS':
1195
									const newKeywords = lp.getRemainingString().split( ',' );
G
Garrett Johnson 已提交
1196

1197
									if ( newKeywords.length > 0 ) {
G
Garrett Johnson 已提交
1198

1199
										if ( ! keywords ) {
G
Garrett Johnson 已提交
1200

1201
											keywords = [];
G
Garrett Johnson 已提交
1202

M
Marco Fugaro 已提交
1203
										}
G
Garrett Johnson 已提交
1204

1205
										newKeywords.forEach( function ( keyword ) {
G
Garrett Johnson 已提交
1206

1207
											keywords.push( keyword.trim() );
G
Garrett Johnson 已提交
1208

1209
										} );
G
Garrett Johnson 已提交
1210

1211
									}
G
Garrett Johnson 已提交
1212

1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229
									break;

								case 'FILE':
									if ( lineIndex > 0 ) {

										// Start embedded text files parsing
										parsingEmbeddedFiles = true;
										currentEmbeddedFileName = lp.getRemainingString();
										currentEmbeddedText = '';
										bfcCertified = false;
										bfcCCW = true;

									}

									break;

								case 'BFC':
M
Michael Herzog 已提交
1230
									// Changes to the backface culling state
1231 1232
									while ( ! lp.isAtTheEnd() ) {

1233
										const token = lp.getToken();
1234 1235

										switch ( token ) {
G
Garrett Johnson 已提交
1236

1237 1238 1239 1240 1241
											case 'CERTIFY':
											case 'NOCERTIFY':
												bfcCertified = token === 'CERTIFY';
												bfcCCW = true;
												break;
G
Garrett Johnson 已提交
1242

1243 1244 1245 1246
											case 'CW':
											case 'CCW':
												bfcCCW = token === 'CCW';
												break;
G
Garrett Johnson 已提交
1247

1248 1249 1250
											case 'INVERTNEXT':
												bfcInverted = true;
												break;
G
Garrett Johnson 已提交
1251

1252 1253 1254 1255
											case 'CLIP':
											case 'NOCLIP':
												bfcCull = token === 'CLIP';
												break;
G
Garrett Johnson 已提交
1256

1257 1258 1259
											default:
												console.warn( 'THREE.LDrawLoader: BFC directive "' + token + '" is unknown.' );
												break;
G
Garrett Johnson 已提交
1260 1261 1262

										}

1263
									}
1264

1265
									break;
Y
yomboprime 已提交
1266

1267 1268 1269
								case 'STEP':
									startingConstructionStep = true;
									break;
Y
yomboprime 已提交
1270

1271
								default:
M
Michael Herzog 已提交
1272
									// Other meta directives are not implemented
1273
									break;
Y
yomboprime 已提交
1274 1275

							}
1276

1277 1278 1279 1280 1281 1282
						}

						break;
						// Line type 1: Sub-object file

					case '1':
1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296
						material = parseColourCode( lp );
						const posX = parseFloat( lp.getToken() );
						const posY = parseFloat( lp.getToken() );
						const posZ = parseFloat( lp.getToken() );
						const m0 = parseFloat( lp.getToken() );
						const m1 = parseFloat( lp.getToken() );
						const m2 = parseFloat( lp.getToken() );
						const m3 = parseFloat( lp.getToken() );
						const m4 = parseFloat( lp.getToken() );
						const m5 = parseFloat( lp.getToken() );
						const m6 = parseFloat( lp.getToken() );
						const m7 = parseFloat( lp.getToken() );
						const m8 = parseFloat( lp.getToken() );
						const matrix = new THREE.Matrix4().set( m0, m1, m2, posX, m3, m4, m5, posY, m6, m7, m8, posZ, 0, 0, 0, 1 );
M
Michael Herzog 已提交
1297
						let fileName = lp.getRemainingString().trim().replace( /\\/g, '/' );
1298 1299 1300 1301 1302 1303 1304

						if ( scope.fileMap[ fileName ] ) {

							// Found the subobject path in the preloaded file path map
							fileName = scope.fileMap[ fileName ];

						} else {
Y
yomboprime 已提交
1305

1306 1307
							// Standardized subfolders
							if ( fileName.startsWith( 's/' ) ) {
Y
yomboprime 已提交
1308

1309
								fileName = 'parts/' + fileName;
G
Garrett Johnson 已提交
1310

1311
							} else if ( fileName.startsWith( '48/' ) ) {
Y
yomboprime 已提交
1312

1313
								fileName = 'p/' + fileName;
Y
yomboprime 已提交
1314

1315
							}
Y
yomboprime 已提交
1316

1317
						}
Y
yomboprime 已提交
1318

1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334
						subobjects.push( {
							material: material,
							matrix: matrix,
							fileName: fileName,
							originalFileName: fileName,
							locationState: FILE_LOCATION_AS_IS,
							url: null,
							triedLowerCase: false,
							inverted: bfcInverted !== currentParseScope.inverted,
							startingConstructionStep: startingConstructionStep
						} );
						bfcInverted = false;
						break;
						// Line type 2: Line segment

					case '2':
1335 1336
						material = parseColourCode( lp, true );
						segment = {
1337 1338 1339 1340 1341 1342 1343 1344 1345 1346
							material: material.userData.edgeMaterial,
							colourCode: material.userData.code,
							v0: parseVector( lp ),
							v1: parseVector( lp )
						};
						lineSegments.push( segment );
						break;
						// Line type 5: Conditional Line segment

					case '5':
1347 1348
						material = parseColourCode( lp, true );
						segment = {
1349 1350 1351 1352 1353 1354 1355 1356 1357 1358
							material: material.userData.edgeMaterial.userData.conditionalEdgeMaterial,
							colourCode: material.userData.code,
							v0: parseVector( lp ),
							v1: parseVector( lp ),
							c0: parseVector( lp ),
							c1: parseVector( lp )
						};
						conditionalSegments.push( segment );
						break;
						// Line type 3: Triangle
Y
yomboprime 已提交
1359

1360
					case '3':
1361 1362 1363 1364
						material = parseColourCode( lp );
						inverted = currentParseScope.inverted;
						ccw = bfcCCW !== inverted;
						doubleSided = ! bfcCertified || ! bfcCull;
Y
yomboprime 已提交
1365

1366
						if ( ccw === true ) {
G
Garrett Johnson 已提交
1367

1368 1369 1370
							v0 = parseVector( lp );
							v1 = parseVector( lp );
							v2 = parseVector( lp );
G
Garrett Johnson 已提交
1371

1372
						} else {
1373

1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393
							v2 = parseVector( lp );
							v1 = parseVector( lp );
							v0 = parseVector( lp );

						}

						_tempVec0.subVectors( v1, v0 );

						_tempVec1.subVectors( v2, v1 );

						faceNormal = new THREE.Vector3().crossVectors( _tempVec0, _tempVec1 ).normalize();
						triangles.push( {
							material: material,
							colourCode: material.userData.code,
							v0: v0,
							v1: v1,
							v2: v2,
							faceNormal: faceNormal,
							n0: null,
							n1: null,
M
Mr.doob 已提交
1394 1395
							n2: null,
							fromQuad: false
1396 1397 1398
						} );

						if ( doubleSided === true ) {
G
Garrett Johnson 已提交
1399 1400 1401 1402 1403

							triangles.push( {
								material: material,
								colourCode: material.userData.code,
								v0: v0,
1404 1405
								v1: v2,
								v2: v1,
G
Garrett Johnson 已提交
1406 1407 1408
								faceNormal: faceNormal,
								n0: null,
								n1: null,
M
Mr.doob 已提交
1409 1410
								n2: null,
								fromQuad: false
G
Garrett Johnson 已提交
1411 1412
							} );

1413
						}
Y
yomboprime 已提交
1414

1415 1416
						break;
						// Line type 4: Quadrilateral
Y
yomboprime 已提交
1417

1418
					case '4':
1419 1420 1421 1422
						material = parseColourCode( lp );
						inverted = currentParseScope.inverted;
						ccw = bfcCCW !== inverted;
						doubleSided = ! bfcCertified || ! bfcCull;
G
Garrett Johnson 已提交
1423

1424
						if ( ccw === true ) {
G
Garrett Johnson 已提交
1425

1426 1427 1428 1429
							v0 = parseVector( lp );
							v1 = parseVector( lp );
							v2 = parseVector( lp );
							v3 = parseVector( lp );
G
Garrett Johnson 已提交
1430

1431
						} else {
G
Garrett Johnson 已提交
1432

1433 1434 1435 1436
							v3 = parseVector( lp );
							v2 = parseVector( lp );
							v1 = parseVector( lp );
							v0 = parseVector( lp );
Y
yomboprime 已提交
1437

1438
						}
Y
yomboprime 已提交
1439

1440 1441 1442 1443
						_tempVec0.subVectors( v1, v0 );

						_tempVec1.subVectors( v2, v1 );

M
Mr.doob 已提交
1444 1445 1446
						faceNormal = new THREE.Vector3().crossVectors( _tempVec0, _tempVec1 ).normalize(); // specifically place the triangle diagonal in the v0 and v1 slots so we can
						// account for the doubling of vertices later when smoothing normals.

1447 1448 1449
						triangles.push( {
							material: material,
							colourCode: material.userData.code,
M
Mr.doob 已提交
1450 1451 1452
							v0: v2,
							v1: v0,
							v2: v1,
1453 1454 1455
							faceNormal: faceNormal,
							n0: null,
							n1: null,
M
Mr.doob 已提交
1456 1457
							n2: null,
							fromQuad: true
1458 1459 1460 1461 1462 1463 1464 1465 1466 1467
						} );
						triangles.push( {
							material: material,
							colourCode: material.userData.code,
							v0: v0,
							v1: v2,
							v2: v3,
							faceNormal: faceNormal,
							n0: null,
							n1: null,
M
Mr.doob 已提交
1468 1469
							n2: null,
							fromQuad: true
1470
						} );
Y
yomboprime 已提交
1471

1472
						if ( doubleSided === true ) {
G
Garrett Johnson 已提交
1473 1474 1475 1476 1477

							triangles.push( {
								material: material,
								colourCode: material.userData.code,
								v0: v0,
1478 1479
								v1: v2,
								v2: v1,
G
Garrett Johnson 已提交
1480 1481 1482
								faceNormal: faceNormal,
								n0: null,
								n1: null,
M
Mr.doob 已提交
1483 1484
								n2: null,
								fromQuad: true
G
Garrett Johnson 已提交
1485 1486 1487 1488
							} );
							triangles.push( {
								material: material,
								colourCode: material.userData.code,
M
Mr.doob 已提交
1489 1490 1491
								v0: v2,
								v1: v0,
								v2: v3,
G
Garrett Johnson 已提交
1492 1493 1494
								faceNormal: faceNormal,
								n0: null,
								n1: null,
M
Mr.doob 已提交
1495 1496
								n2: null,
								fromQuad: true
G
Garrett Johnson 已提交
1497 1498
							} );

1499
						}
1500

1501
						break;
Y
yomboprime 已提交
1502

1503 1504 1505
					default:
						throw 'LDrawLoader: Unknown line type "' + lineType + '"' + lp.getLineNumberString() + '.';
						break;
Y
yomboprime 已提交
1506

M
Marco Fugaro 已提交
1507
				}
Y
yomboprime 已提交
1508

1509
			}
1510

1511
			if ( parsingEmbeddedFiles ) {
Y
yomboprime 已提交
1512

1513
				this.subobjectCache[ currentEmbeddedFileName.toLowerCase() ] = currentEmbeddedText;
G
Garrett Johnson 已提交
1514

1515
			}
Y
yomboprime 已提交
1516

1517 1518 1519 1520 1521
			currentParseScope.category = category;
			currentParseScope.keywords = keywords;
			currentParseScope.subobjects = subobjects;
			currentParseScope.numSubobjects = subobjects.length;
			currentParseScope.subobjectIndex = 0;
Y
yomboprime 已提交
1522

1523
		}
Y
yomboprime 已提交
1524

1525
		computeConstructionSteps( model ) {
Y
yomboprime 已提交
1526

1527
			// Sets userdata.constructionStep number in THREE.Group objects and userData.numConstructionSteps number in the root THREE.Group object.
1528
			let stepNumber = 0;
1529
			model.traverse( c => {
Y
yomboprime 已提交
1530

1531
				if ( c.isGroup ) {
Y
yomboprime 已提交
1532

1533
					if ( c.userData.startingConstructionStep ) {
Y
yomboprime 已提交
1534

1535
						stepNumber ++;
Y
yomboprime 已提交
1536 1537 1538

					}

1539
					c.userData.constructionStep = stepNumber;
Y
yomboprime 已提交
1540

1541
				}
Y
yomboprime 已提交
1542

1543 1544
			} );
			model.userData.numConstructionSteps = stepNumber + 1;
Y
yomboprime 已提交
1545

1546
		}
Y
yomboprime 已提交
1547

1548
		processObject( text, onProcessed, subobject, url ) {
Y
yomboprime 已提交
1549

1550 1551
			const scope = this;
			const parseScope = scope.newParseScopeLevel();
1552
			parseScope.url = url;
1553
			const parentParseScope = scope.getParentParseScope(); // Set current matrix
Y
yomboprime 已提交
1554

1555
			if ( subobject ) {
Y
yomboprime 已提交
1556

1557 1558 1559 1560
				parseScope.currentMatrix.multiplyMatrices( parentParseScope.currentMatrix, subobject.matrix );
				parseScope.matrix.copy( subobject.matrix );
				parseScope.inverted = subobject.inverted;
				parseScope.startingConstructionStep = subobject.startingConstructionStep;
Y
yomboprime 已提交
1561

1562
			} // Add to cache
Y
yomboprime 已提交
1563 1564


1565
			let currentFileName = parentParseScope.currentFileName;
Y
yomboprime 已提交
1566

1567
			if ( currentFileName !== null ) {
Y
yomboprime 已提交
1568

1569
				currentFileName = parentParseScope.currentFileName.toLowerCase();
Y
yomboprime 已提交
1570

1571
			}
Y
yomboprime 已提交
1572

1573
			if ( scope.subobjectCache[ currentFileName ] === undefined ) {
Y
yomboprime 已提交
1574

1575
				scope.subobjectCache[ currentFileName ] = text;
Y
yomboprime 已提交
1576

1577
			} // Parse the object (returns a THREE.Group)
Y
yomboprime 已提交
1578 1579


1580
			scope.objectParse( text );
1581
			let finishedCount = 0;
1582
			onSubobjectFinish();
Y
yomboprime 已提交
1583

1584
			function onSubobjectFinish() {
Y
yomboprime 已提交
1585

1586
				finishedCount ++;
Y
yomboprime 已提交
1587

1588
				if ( finishedCount === parseScope.subobjects.length + 1 ) {
Y
yomboprime 已提交
1589

1590
					finalizeObject();
Y
yomboprime 已提交
1591

1592
				} else {
Y
yomboprime 已提交
1593

1594 1595 1596 1597 1598 1599
					// Once the previous subobject has finished we can start processing the next one in the list.
					// The subobject processing shares scope in processing so it's important that they be loaded serially
					// to avoid race conditions.
					// Promise.resolve is used as an approach to asynchronously schedule a task _before_ this frame ends to
					// avoid stack overflow exceptions when loading many subobjects from the cache. RequestAnimationFrame
					// will work but causes the load to happen after the next frame which causes the load to take significantly longer.
1600
					const subobject = parseScope.subobjects[ parseScope.subobjectIndex ];
1601
					Promise.resolve().then( function () {
Y
yomboprime 已提交
1602

1603
						loadSubobject( subobject );
Y
yomboprime 已提交
1604

1605 1606
					} );
					parseScope.subobjectIndex ++;
Y
yomboprime 已提交
1607

1608
				}
Y
yomboprime 已提交
1609

1610
			}
Y
yomboprime 已提交
1611

1612
			function finalizeObject() {
Y
yomboprime 已提交
1613

1614
				if ( scope.smoothNormals && parseScope.type === 'Part' ) {
Y
yomboprime 已提交
1615

1616
					smoothNormals( parseScope.triangles, parseScope.lineSegments );
Y
yomboprime 已提交
1617

1618
				}
M
Mugen87 已提交
1619

1620
				const isRoot = ! parentParseScope.isFromParse;
Y
yomboprime 已提交
1621

1622
				if ( scope.separateObjects && ! isPrimitiveType( parseScope.type ) || isRoot ) {
Y
yomboprime 已提交
1623

1624
					const objGroup = parseScope.groupObject;
Y
yomboprime 已提交
1625

1626
					if ( parseScope.triangles.length > 0 ) {
Y
yomboprime 已提交
1627

1628
						objGroup.add( createObject( parseScope.triangles, 3 ) );
Y
yomboprime 已提交
1629

1630
					}
Y
yomboprime 已提交
1631

1632
					if ( parseScope.lineSegments.length > 0 ) {
Y
yomboprime 已提交
1633

1634
						objGroup.add( createObject( parseScope.lineSegments, 2 ) );
Y
yomboprime 已提交
1635

1636
					}
Y
yomboprime 已提交
1637

1638
					if ( parseScope.conditionalSegments.length > 0 ) {
Y
yomboprime 已提交
1639

1640
						objGroup.add( createObject( parseScope.conditionalSegments, 2, true ) );
Y
yomboprime 已提交
1641

1642
					}
Y
yomboprime 已提交
1643

1644
					if ( parentParseScope.groupObject ) {
Y
yomboprime 已提交
1645

1646 1647 1648 1649 1650
						objGroup.name = parseScope.fileName;
						objGroup.userData.category = parseScope.category;
						objGroup.userData.keywords = parseScope.keywords;
						parseScope.matrix.decompose( objGroup.position, objGroup.quaternion, objGroup.scale );
						parentParseScope.groupObject.add( objGroup );
Y
yomboprime 已提交
1651

1652
					}
Y
yomboprime 已提交
1653

1654
				} else {
Y
yomboprime 已提交
1655

1656 1657 1658 1659 1660 1661 1662
					const separateObjects = scope.separateObjects;
					const parentLineSegments = parentParseScope.lineSegments;
					const parentConditionalSegments = parentParseScope.conditionalSegments;
					const parentTriangles = parentParseScope.triangles;
					const lineSegments = parseScope.lineSegments;
					const conditionalSegments = parseScope.conditionalSegments;
					const triangles = parseScope.triangles;
Y
yomboprime 已提交
1663

1664
					for ( let i = 0, l = lineSegments.length; i < l; i ++ ) {
M
Mugen87 已提交
1665

1666
						const ls = lineSegments[ i ];
1667 1668

						if ( separateObjects ) {
Y
yomboprime 已提交
1669

1670 1671
							ls.v0.applyMatrix4( parseScope.matrix );
							ls.v1.applyMatrix4( parseScope.matrix );
Y
yomboprime 已提交
1672 1673

						}
M
Mugen87 已提交
1674

1675
						parentLineSegments.push( ls );
Y
yomboprime 已提交
1676

1677
					}
Y
yomboprime 已提交
1678

1679
					for ( let i = 0, l = conditionalSegments.length; i < l; i ++ ) {
Y
yomboprime 已提交
1680

1681
						const os = conditionalSegments[ i ];
M
Mugen87 已提交
1682

1683
						if ( separateObjects ) {
Y
yomboprime 已提交
1684

1685 1686 1687 1688
							os.v0.applyMatrix4( parseScope.matrix );
							os.v1.applyMatrix4( parseScope.matrix );
							os.c0.applyMatrix4( parseScope.matrix );
							os.c1.applyMatrix4( parseScope.matrix );
Y
yomboprime 已提交
1689 1690

						}
M
Mugen87 已提交
1691

1692
						parentConditionalSegments.push( os );
Y
yomboprime 已提交
1693

1694
					}
Y
yomboprime 已提交
1695

1696
					for ( let i = 0, l = triangles.length; i < l; i ++ ) {
M
Mugen87 已提交
1697

1698
						const tri = triangles[ i ];
Y
yomboprime 已提交
1699

1700
						if ( separateObjects ) {
Y
yomboprime 已提交
1701

1702 1703 1704
							tri.v0 = tri.v0.clone().applyMatrix4( parseScope.matrix );
							tri.v1 = tri.v1.clone().applyMatrix4( parseScope.matrix );
							tri.v2 = tri.v2.clone().applyMatrix4( parseScope.matrix );
Y
yomboprime 已提交
1705

1706
							_tempVec0.subVectors( tri.v1, tri.v0 );
M
Mugen87 已提交
1707

1708
							_tempVec1.subVectors( tri.v2, tri.v1 );
Y
yomboprime 已提交
1709

1710
							tri.faceNormal.crossVectors( _tempVec0, _tempVec1 ).normalize();
Y
yomboprime 已提交
1711

1712
						}
Y
yomboprime 已提交
1713

1714
						parentTriangles.push( tri );
Y
yomboprime 已提交
1715

M
Marco Fugaro 已提交
1716
					}
Y
yomboprime 已提交
1717

1718
				}
Y
yomboprime 已提交
1719

1720
				scope.removeScopeLevel(); // If it is root object, compute construction steps
Y
yomboprime 已提交
1721

1722
				if ( ! parentParseScope.isFromParse ) {
Y
yomboprime 已提交
1723

1724
					scope.computeConstructionSteps( parseScope.groupObject );
Y
yomboprime 已提交
1725

1726
				}
Y
yomboprime 已提交
1727

1728
				if ( onProcessed ) {
Y
yomboprime 已提交
1729

1730
					onProcessed( parseScope.groupObject );
Y
yomboprime 已提交
1731

1732
				}
Y
yomboprime 已提交
1733

1734
			}
Y
yomboprime 已提交
1735

1736
			function loadSubobject( subobject ) {
Y
yomboprime 已提交
1737

1738 1739 1740
				parseScope.mainColourCode = subobject.material.userData.code;
				parseScope.mainEdgeColourCode = subobject.material.userData.edgeMaterial.userData.code;
				parseScope.currentFileName = subobject.originalFileName; // If subobject was cached previously, use the cached one
Y
yomboprime 已提交
1741

1742
				const cached = scope.subobjectCache[ subobject.originalFileName.toLowerCase() ];
Y
yomboprime 已提交
1743

1744
				if ( cached ) {
Y
yomboprime 已提交
1745

1746
					scope.processObject( cached, function ( subobjectGroup ) {
Y
yomboprime 已提交
1747

1748 1749
						onSubobjectLoaded( subobjectGroup, subobject );
						onSubobjectFinish();
Y
yomboprime 已提交
1750

1751 1752
					}, subobject, url );
					return;
Y
yomboprime 已提交
1753

1754 1755
				} // Adjust file name to locate the subobject file path in standard locations (always under directory scope.path)
				// Update also subobject.locationState for the next try if this load fails.
Y
yomboprime 已提交
1756 1757


1758 1759
				let subobjectURL = subobject.fileName;
				let newLocationState = FILE_LOCATION_NOT_FOUND;
Y
yomboprime 已提交
1760

1761
				switch ( subobject.locationState ) {
Y
yomboprime 已提交
1762

1763 1764 1765
					case FILE_LOCATION_AS_IS:
						newLocationState = subobject.locationState + 1;
						break;
Y
yomboprime 已提交
1766

1767 1768 1769 1770
					case FILE_LOCATION_TRY_PARTS:
						subobjectURL = 'parts/' + subobjectURL;
						newLocationState = subobject.locationState + 1;
						break;
Y
yomboprime 已提交
1771

1772 1773 1774 1775
					case FILE_LOCATION_TRY_P:
						subobjectURL = 'p/' + subobjectURL;
						newLocationState = subobject.locationState + 1;
						break;
Y
yomboprime 已提交
1776

1777 1778 1779 1780
					case FILE_LOCATION_TRY_MODELS:
						subobjectURL = 'models/' + subobjectURL;
						newLocationState = subobject.locationState + 1;
						break;
M
Mugen87 已提交
1781

1782 1783 1784 1785
					case FILE_LOCATION_TRY_RELATIVE:
						subobjectURL = url.substring( 0, url.lastIndexOf( '/' ) + 1 ) + subobjectURL;
						newLocationState = subobject.locationState + 1;
						break;
Y
yomboprime 已提交
1786

1787 1788
					case FILE_LOCATION_TRY_ABSOLUTE:
						if ( subobject.triedLowerCase ) {
Y
yomboprime 已提交
1789

1790 1791
							// Try absolute path
							newLocationState = FILE_LOCATION_NOT_FOUND;
Y
yomboprime 已提交
1792

1793
						} else {
Y
yomboprime 已提交
1794

1795 1796 1797 1798 1799
							// Next attempt is lower case
							subobject.fileName = subobject.fileName.toLowerCase();
							subobjectURL = subobject.fileName;
							subobject.triedLowerCase = true;
							newLocationState = FILE_LOCATION_AS_IS;
Y
yomboprime 已提交
1800

1801
						}
Y
yomboprime 已提交
1802

1803
						break;
Y
yomboprime 已提交
1804

1805
					case FILE_LOCATION_NOT_FOUND:
M
Michael Herzog 已提交
1806
						// All location possibilities have been tried, give up loading this object
1807 1808
						console.warn( 'LDrawLoader: Subobject "' + subobject.originalFileName + '" could not be found.' );
						return;
Y
yomboprime 已提交
1809

1810
				}
Y
yomboprime 已提交
1811

1812 1813 1814 1815
				subobject.locationState = newLocationState;
				subobject.url = subobjectURL; // Load the subobject
				// Use another file loader here so we can keep track of the subobject information
				// and use it when processing the next model.
Y
yomboprime 已提交
1816

1817
				const fileLoader = new THREE.FileLoader( scope.manager );
1818 1819 1820 1821
				fileLoader.setPath( scope.path );
				fileLoader.setRequestHeader( scope.requestHeader );
				fileLoader.setWithCredentials( scope.withCredentials );
				fileLoader.load( subobjectURL, function ( text ) {
Y
yomboprime 已提交
1822

1823
					scope.processObject( text, function ( subobjectGroup ) {
Y
yomboprime 已提交
1824

1825 1826
						onSubobjectLoaded( subobjectGroup, subobject );
						onSubobjectFinish();
Y
yomboprime 已提交
1827

1828
					}, subobject, url );
Y
yomboprime 已提交
1829

1830
				}, undefined, function ( err ) {
Y
yomboprime 已提交
1831

1832
					onSubobjectError( err, subobject );
Y
yomboprime 已提交
1833

1834
				}, subobject );
Y
yomboprime 已提交
1835

1836
			}
Y
yomboprime 已提交
1837

1838
			function onSubobjectLoaded( subobjectGroup, subobject ) {
Y
yomboprime 已提交
1839

1840
				if ( subobjectGroup === null ) {
Y
yomboprime 已提交
1841

1842
					// Try to reload
M
Marco Fugaro 已提交
1843
					loadSubobject( subobject );
1844
					return;
Y
yomboprime 已提交
1845

M
Marco Fugaro 已提交
1846
				}
Y
yomboprime 已提交
1847

1848 1849
				scope.fileMap[ subobject.originalFileName ] = subobject.url;

Y
yomboprime 已提交
1850 1851
			}

1852 1853 1854 1855 1856 1857 1858 1859 1860 1861
			function onSubobjectError( err, subobject ) {

				// Retry download from a different default possible location
				loadSubobject( subobject );

			}

		}

	}
Y
yomboprime 已提交
1862

M
Marco Fugaro 已提交
1863
	THREE.LDrawLoader = LDrawLoader;
Y
yomboprime 已提交
1864 1865

} )();