Skip to content

Commit e8d4219

Browse files
authored
Merge pull request #16245 from gkjohnson/ldraw-fixes
LDrawLoader: Fix stack overflow exceptions
2 parents 0124dc9 + 4a683e2 commit e8d4219

File tree

1 file changed

+48
-76
lines changed

1 file changed

+48
-76
lines changed

examples/js/loaders/LDrawLoader.js

Lines changed: 48 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -271,18 +271,6 @@ THREE.LDrawLoader = ( function () {
271271

272272
}, onProgress, onError );
273273

274-
function subobjectLoad( url, onLoad, onProgress, onError, subobject ) {
275-
276-
var fileLoader = new THREE.FileLoader( scope.manager );
277-
fileLoader.setPath( scope.path );
278-
fileLoader.load( url, function ( text ) {
279-
280-
processObject( text, onLoad, subobject );
281-
282-
}, onProgress, onError );
283-
284-
}
285-
286274
function processObject( text, onProcessed, subobject ) {
287275

288276
var parseScope = scope.newParseScopeLevel();
@@ -292,6 +280,12 @@ THREE.LDrawLoader = ( function () {
292280

293281
// Add to cache
294282
var currentFileName = parentParseScope.currentFileName;
283+
if ( currentFileName !== null ) {
284+
285+
currentFileName = parentParseScope.currentFileName.toLowerCase();
286+
287+
}
288+
295289
if ( scope.subobjectCache[ currentFileName ] === undefined ) {
296290

297291
scope.subobjectCache[ currentFileName ] = text;
@@ -308,37 +302,39 @@ THREE.LDrawLoader = ( function () {
308302
parseScope.numSubobjects = parseScope.subobjects.length;
309303
parseScope.subobjectIndex = 0;
310304

311-
if ( parseScope.numSubobjects > 0 ) {
305+
var finishedCount = 0;
306+
onSubobjectFinish();
312307

313-
// Load the first subobject
314-
var subobjectGroup = loadSubobject( parseScope.subobjects[ 0 ], true );
308+
return objGroup;
315309

316-
// Optimization for loading pack: If subobjects are obtained from cache, keep loading them iteratively rather than recursively
317-
if ( subobjectGroup ) {
310+
function onSubobjectFinish() {
318311

319-
while ( subobjectGroup && parseScope.subobjectIndex < parseScope.numSubobjects - 1 ) {
312+
finishedCount ++;
320313

321-
subobjectGroup = loadSubobject( parseScope.subobjects[ ++ parseScope.subobjectIndex ], true );
314+
if ( finishedCount === parseScope.subobjects.length + 1 ) {
322315

323-
}
324-
325-
if ( subobjectGroup ) {
316+
finalizeObject();
326317

327-
finalizeObject();
318+
} else {
328319

329-
}
320+
// Once the previous subobject has finished we can start processing the next one in the list.
321+
// The subobject processing shares scope in processing so it's important that they be loaded serially
322+
// to avoid race conditions.
323+
// Promise.resolve is used as an approach to asynchronously schedule a task _before_ this frame ends to
324+
// avoid stack overflow exceptions when loading many subobjects from the cache. RequestAnimationFrame
325+
// will work but causes the load to happen after the next frame which causes the load to take significantly longer.
326+
var subobject = parseScope.subobjects[ parseScope.subobjectIndex ];
327+
Promise.resolve().then( function () {
330328

331-
}
329+
loadSubobject( subobject );
332330

333-
} else {
331+
} );
332+
parseScope.subobjectIndex ++;
334333

335-
// No subobjects, finish object
336-
finalizeObject();
334+
}
337335

338336
}
339337

340-
return objGroup;
341-
342338
function finalizeObject() {
343339

344340
if ( ! scope.separateObjects && ! parentParseScope.isFromParse ) {
@@ -368,7 +364,7 @@ THREE.LDrawLoader = ( function () {
368364

369365
}
370366

371-
function loadSubobject( subobject, sync ) {
367+
function loadSubobject( subobject ) {
372368

373369
parseScope.mainColourCode = subobject.material.userData.code;
374370
parseScope.mainEdgeColourCode = subobject.material.userData.edgeMaterial.userData.code;
@@ -382,16 +378,15 @@ THREE.LDrawLoader = ( function () {
382378
}
383379

384380
// If subobject was cached previously, use the cached one
385-
var cached = scope.subobjectCache[ subobject.originalFileName ];
381+
var cached = scope.subobjectCache[ subobject.originalFileName.toLowerCase() ];
386382
if ( cached ) {
387383

388-
var subobjectGroup = processObject( cached, sync ? undefined : onSubobjectLoaded, subobject );
389-
if ( sync ) {
384+
processObject( cached, function ( subobjectGroup ) {
390385

391-
addSubobject( subobject, subobjectGroup );
392-
return subobjectGroup;
386+
onSubobjectLoaded( subobjectGroup, subobject );
387+
onSubobjectFinish();
393388

394-
}
389+
}, subobject );
395390

396391
return;
397392

@@ -451,22 +446,6 @@ THREE.LDrawLoader = ( function () {
451446
// All location possibilities have been tried, give up loading this object
452447
console.warn( 'LDrawLoader: Subobject "' + subobject.originalFileName + '" could not be found.' );
453448

454-
// Try to read the next subobject
455-
parseScope.subobjectIndex ++;
456-
457-
if ( parseScope.subobjectIndex >= parseScope.numSubobjects ) {
458-
459-
// All subojects have been loaded. Finish parent object
460-
scope.removeScopeLevel();
461-
onProcessed( objGroup );
462-
463-
} else {
464-
465-
// Load next subobject
466-
loadSubobject( parseScope.subobjects[ parseScope.subobjectIndex ] );
467-
468-
}
469-
470449
return;
471450

472451
}
@@ -481,15 +460,22 @@ THREE.LDrawLoader = ( function () {
481460
fileLoader.setPath( scope.path );
482461
fileLoader.load( subobjectURL, function ( text ) {
483462

484-
processObject( text, onSubobjectLoaded, subobject );
463+
processObject( text, function ( subobjectGroup ) {
485464

486-
}, undefined, onSubobjectError );
465+
onSubobjectLoaded( subobjectGroup, subobject );
466+
onSubobjectFinish();
487467

488-
}
468+
}, subobject );
469+
470+
}, undefined, function ( err ) {
471+
472+
onSubobjectError( err, subobject );
473+
474+
}, subobject );
489475

490-
function onSubobjectLoaded( subobjectGroup ) {
476+
}
491477

492-
var subobject = parseScope.subobjects[ parseScope.subobjectIndex ];
478+
function onSubobjectLoaded( subobjectGroup, subobject ) {
493479

494480
if ( subobjectGroup === null ) {
495481

@@ -502,20 +488,6 @@ THREE.LDrawLoader = ( function () {
502488
// Add the subobject just loaded
503489
addSubobject( subobject, subobjectGroup );
504490

505-
// Proceed to load the next subobject, or finish the parent object
506-
507-
parseScope.subobjectIndex ++;
508-
509-
if ( parseScope.subobjectIndex < parseScope.numSubobjects ) {
510-
511-
loadSubobject( parseScope.subobjects[ parseScope.subobjectIndex ] );
512-
513-
} else {
514-
515-
finalizeObject();
516-
517-
}
518-
519491
}
520492

521493
function addSubobject( subobject, subobjectGroup ) {
@@ -533,10 +505,10 @@ THREE.LDrawLoader = ( function () {
533505

534506
}
535507

536-
function onSubobjectError( err ) {
508+
function onSubobjectError( err, subobject ) {
537509

538510
// Retry download from a different default possible location
539-
loadSubobject( parseScope.subobjects[ parseScope.subobjectIndex ] );
511+
loadSubobject( subobject );
540512

541513
}
542514

@@ -1071,7 +1043,7 @@ THREE.LDrawLoader = ( function () {
10711043
if ( line.startsWith( '0 FILE ' ) ) {
10721044

10731045
// Save previous embedded file in the cache
1074-
this.subobjectCache[ currentEmbeddedFileName ] = currentEmbeddedText;
1046+
this.subobjectCache[ currentEmbeddedFileName.toLowerCase() ] = currentEmbeddedText;
10751047

10761048
// New embedded text file
10771049
currentEmbeddedFileName = line.substring( 7 );
@@ -1436,7 +1408,7 @@ THREE.LDrawLoader = ( function () {
14361408

14371409
if ( parsingEmbeddedFiles ) {
14381410

1439-
this.subobjectCache[ currentEmbeddedFileName ] = currentEmbeddedText;
1411+
this.subobjectCache[ currentEmbeddedFileName.toLowerCase() ] = currentEmbeddedText;
14401412

14411413
}
14421414

0 commit comments

Comments
 (0)