|
@@ -477,6 +477,115 @@ class LineParser {
|
|
|
|
|
|
}
|
|
|
|
|
|
+class LDrawFileCache {
|
|
|
+
|
|
|
+ constructor( loader ) {
|
|
|
+
|
|
|
+ this.cache = {};
|
|
|
+ this.loader = loader;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ setData( key, contents ) {
|
|
|
+
|
|
|
+ this.cache[ key.toLowerCase() ] = contents;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ async loadData( fileName ) {
|
|
|
+
|
|
|
+ const key = fileName.toLowerCase();
|
|
|
+ if ( key in this.cache ) {
|
|
|
+
|
|
|
+ return this.cache[ key ];
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ this.cache[ fileName ] = new Promise( async ( resolve, reject ) => {
|
|
|
+
|
|
|
+ let triedLowerCase = false;
|
|
|
+ let locationState = FILE_LOCATION_AS_IS;
|
|
|
+ while ( locationState !== FILE_LOCATION_NOT_FOUND ) {
|
|
|
+
|
|
|
+ let subobjectURL = fileName;
|
|
|
+ switch ( locationState ) {
|
|
|
+
|
|
|
+ case FILE_LOCATION_AS_IS:
|
|
|
+ locationState = locationState + 1;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case FILE_LOCATION_TRY_PARTS:
|
|
|
+ subobjectURL = 'parts/' + subobjectURL;
|
|
|
+ locationState = locationState + 1;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case FILE_LOCATION_TRY_P:
|
|
|
+ subobjectURL = 'p/' + subobjectURL;
|
|
|
+ locationState = locationState + 1;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case FILE_LOCATION_TRY_MODELS:
|
|
|
+ subobjectURL = 'models/' + subobjectURL;
|
|
|
+ locationState = locationState + 1;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case FILE_LOCATION_TRY_RELATIVE:
|
|
|
+ subobjectURL = fileName.substring( 0, fileName.lastIndexOf( '/' ) + 1 ) + subobjectURL;
|
|
|
+ locationState = locationState + 1;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case FILE_LOCATION_TRY_ABSOLUTE:
|
|
|
+
|
|
|
+ if ( triedLowerCase ) {
|
|
|
+
|
|
|
+ // Try absolute path
|
|
|
+ locationState = FILE_LOCATION_NOT_FOUND;
|
|
|
+
|
|
|
+ } else {
|
|
|
+
|
|
|
+ // Next attempt is lower case
|
|
|
+ fileName = fileName.toLowerCase();
|
|
|
+ subobjectURL = fileName;
|
|
|
+ triedLowerCase = true;
|
|
|
+ locationState = FILE_LOCATION_AS_IS;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ break;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ const loader = this.loader;
|
|
|
+ const fileLoader = new FileLoader( loader.manager );
|
|
|
+ fileLoader.setPath( loader.partsLibraryPath );
|
|
|
+ fileLoader.setRequestHeader( loader.requestHeader );
|
|
|
+ fileLoader.setWithCredentials( loader.withCredentials );
|
|
|
+
|
|
|
+ try {
|
|
|
+
|
|
|
+ const text = await fileLoader.loadAsync( subobjectURL );
|
|
|
+ this.setData( fileName, text );
|
|
|
+ resolve( text );
|
|
|
+ return;
|
|
|
+
|
|
|
+ } catch {
|
|
|
+
|
|
|
+ continue;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ reject();
|
|
|
+
|
|
|
+ } );
|
|
|
+
|
|
|
+ return this.cache[ fileName ];
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
function sortByMaterial( a, b ) {
|
|
|
|
|
|
if ( a.colourCode === b.colourCode ) {
|
|
@@ -687,23 +796,19 @@ class LDrawLoader extends Loader {
|
|
|
|
|
|
super( manager );
|
|
|
|
|
|
- // This is a stack of 'parse scopes' with one level per subobject loaded file.
|
|
|
- // 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.
|
|
|
- this.parseScopesStack = null;
|
|
|
-
|
|
|
// Array of THREE.Material
|
|
|
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)
|
|
|
- this.subobjectCache = {};
|
|
|
+ this.cache = new LDrawFileCache( this );
|
|
|
|
|
|
// 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.
|
|
|
this.fileMap = null;
|
|
|
|
|
|
- // Add default main triangle and line edge materials (used in piecess that can be coloured with a main color)
|
|
|
+ this.rootParseScope = this.newParseScopeLevel();
|
|
|
+
|
|
|
+ // Add default main triangle and line edge materials (used in pieces that can be coloured with a main color)
|
|
|
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' ) )
|
|
@@ -772,7 +877,12 @@ class LDrawLoader extends Loader {
|
|
|
fileLoader.setWithCredentials( this.withCredentials );
|
|
|
fileLoader.load( url, function ( text ) {
|
|
|
|
|
|
- scope.processObject( text, onLoad, null, url );
|
|
|
+ scope.processObject( text, null, url, scope.rootParseScope )
|
|
|
+ .then( function ( result ) {
|
|
|
+
|
|
|
+ onLoad( result.groupObject );
|
|
|
+
|
|
|
+ } );
|
|
|
|
|
|
}, onProgress, onError );
|
|
|
|
|
@@ -781,20 +891,20 @@ class LDrawLoader extends Loader {
|
|
|
parse( text, path, onLoad ) {
|
|
|
|
|
|
// Async parse. This function calls onParse with the parsed THREE.Object3D as parameter
|
|
|
+ this.processObject( text, null, path, this.rootParseScope )
|
|
|
+ .then( function ( result ) {
|
|
|
+
|
|
|
+ onLoad( result.groupObject );
|
|
|
|
|
|
- this.processObject( text, onLoad, null, path );
|
|
|
+ } );
|
|
|
|
|
|
}
|
|
|
|
|
|
setMaterials( materials ) {
|
|
|
|
|
|
// Clears parse scopes stack, adds new scope with material library
|
|
|
-
|
|
|
- this.parseScopesStack = [];
|
|
|
-
|
|
|
- this.newParseScopeLevel( materials );
|
|
|
-
|
|
|
- this.getCurrentParseScope().isFromParse = false;
|
|
|
+ this.rootParseScope = this.newParseScopeLevel( materials );
|
|
|
+ this.rootParseScope.isFromParse = false;
|
|
|
|
|
|
this.materials = materials;
|
|
|
|
|
@@ -810,7 +920,7 @@ class LDrawLoader extends Loader {
|
|
|
|
|
|
}
|
|
|
|
|
|
- newParseScopeLevel( materials ) {
|
|
|
+ newParseScopeLevel( materials = null, parentScope = null ) {
|
|
|
|
|
|
// Adds a new scope level, assign materials to it and returns it
|
|
|
|
|
@@ -827,9 +937,9 @@ class LDrawLoader extends Loader {
|
|
|
|
|
|
}
|
|
|
|
|
|
- const topParseScope = this.getCurrentParseScope();
|
|
|
const newParseScope = {
|
|
|
|
|
|
+ parentScope: parentScope,
|
|
|
lib: matLib,
|
|
|
url: null,
|
|
|
|
|
@@ -843,8 +953,8 @@ class LDrawLoader extends Loader {
|
|
|
|
|
|
// Current subobject
|
|
|
currentFileName: null,
|
|
|
- mainColourCode: topParseScope ? topParseScope.mainColourCode : '16',
|
|
|
- mainEdgeColourCode: topParseScope ? topParseScope.mainEdgeColourCode : '24',
|
|
|
+ mainColourCode: parentScope ? parentScope.mainColourCode : '16',
|
|
|
+ mainEdgeColourCode: parentScope ? parentScope.mainEdgeColourCode : '24',
|
|
|
currentMatrix: new Matrix4(),
|
|
|
matrix: new Matrix4(),
|
|
|
|
|
@@ -860,25 +970,15 @@ class LDrawLoader extends Loader {
|
|
|
startingConstructionStep: false
|
|
|
};
|
|
|
|
|
|
- this.parseScopesStack.push( newParseScope );
|
|
|
-
|
|
|
return newParseScope;
|
|
|
|
|
|
}
|
|
|
|
|
|
- removeScopeLevel() {
|
|
|
-
|
|
|
- this.parseScopesStack.pop();
|
|
|
-
|
|
|
- return this;
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- addMaterial( material ) {
|
|
|
+ addMaterial( material, parseScope ) {
|
|
|
|
|
|
// 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;
|
|
|
+ const matLib = parseScope.lib;
|
|
|
|
|
|
if ( ! matLib[ material.userData.code ] ) {
|
|
|
|
|
@@ -892,7 +992,7 @@ class LDrawLoader extends Loader {
|
|
|
|
|
|
}
|
|
|
|
|
|
- getMaterial( colourCode ) {
|
|
|
+ getMaterial( colourCode, parseScope = this.rootParseScope ) {
|
|
|
|
|
|
// Given a colour code search its material in the parse scopes stack
|
|
|
|
|
@@ -906,50 +1006,30 @@ class LDrawLoader extends Loader {
|
|
|
|
|
|
}
|
|
|
|
|
|
- for ( let i = this.parseScopesStack.length - 1; i >= 0; i -- ) {
|
|
|
+ while ( parseScope ) {
|
|
|
|
|
|
- const material = this.parseScopesStack[ i ].lib[ colourCode ];
|
|
|
+ const material = parseScope.lib[ colourCode ];
|
|
|
|
|
|
if ( material ) {
|
|
|
|
|
|
return material;
|
|
|
|
|
|
- }
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- // Material was not found
|
|
|
- return null;
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- getParentParseScope() {
|
|
|
-
|
|
|
- if ( this.parseScopesStack.length > 1 ) {
|
|
|
-
|
|
|
- return this.parseScopesStack[ this.parseScopesStack.length - 2 ];
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- return null;
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- getCurrentParseScope() {
|
|
|
+ } else {
|
|
|
|
|
|
- if ( this.parseScopesStack.length > 0 ) {
|
|
|
+ parseScope = parseScope.parentScope;
|
|
|
|
|
|
- return this.parseScopesStack[ this.parseScopesStack.length - 1 ];
|
|
|
+ }
|
|
|
|
|
|
}
|
|
|
|
|
|
+ // Material was not found
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
parseColourMetaDirective( lineParser ) {
|
|
|
|
|
|
- // Parses a colour definition and returns a THREE.Material or null if error
|
|
|
+ // Parses a colour definition and returns a THREE.Material
|
|
|
|
|
|
let code = null;
|
|
|
|
|
@@ -1200,16 +1280,16 @@ class LDrawLoader extends Loader {
|
|
|
|
|
|
//
|
|
|
|
|
|
- objectParse( text ) {
|
|
|
+ objectParse( text, parseScope ) {
|
|
|
|
|
|
// Retrieve data from the parent parse scope
|
|
|
- const parentParseScope = this.getParentParseScope();
|
|
|
+ const currentParseScope = parseScope;
|
|
|
+ const parentParseScope = currentParseScope.parentScope;
|
|
|
|
|
|
// Main colour codes passed to this subobject (or default codes 16 and 24 if it is the root object)
|
|
|
- const mainColourCode = parentParseScope.mainColourCode;
|
|
|
- const mainEdgeColourCode = parentParseScope.mainEdgeColourCode;
|
|
|
+ const mainColourCode = currentParseScope.mainColourCode;
|
|
|
+ const mainEdgeColourCode = currentParseScope.mainEdgeColourCode;
|
|
|
|
|
|
- const currentParseScope = this.getCurrentParseScope();
|
|
|
|
|
|
// Parse result variables
|
|
|
let faces;
|
|
@@ -1262,7 +1342,7 @@ class LDrawLoader extends Loader {
|
|
|
|
|
|
}
|
|
|
|
|
|
- const material = scope.getMaterial( colourCode );
|
|
|
+ const material = scope.getMaterial( colourCode, currentParseScope );
|
|
|
|
|
|
if ( ! material ) {
|
|
|
|
|
@@ -1300,7 +1380,7 @@ class LDrawLoader extends Loader {
|
|
|
if ( line.startsWith( '0 FILE ' ) ) {
|
|
|
|
|
|
// Save previous embedded file in the cache
|
|
|
- this.subobjectCache[ currentEmbeddedFileName.toLowerCase() ] = currentEmbeddedText;
|
|
|
+ this.cache.setData( currentEmbeddedFileName.toLowerCase(), currentEmbeddedText );
|
|
|
|
|
|
// New embedded text file
|
|
|
currentEmbeddedFileName = line.substring( 7 );
|
|
@@ -1390,7 +1470,7 @@ class LDrawLoader extends Loader {
|
|
|
material = this.parseColourMetaDirective( lp );
|
|
|
if ( material ) {
|
|
|
|
|
|
- this.addMaterial( material );
|
|
|
+ this.addMaterial( material, parseScope );
|
|
|
|
|
|
} else {
|
|
|
|
|
@@ -1539,7 +1619,7 @@ class LDrawLoader extends Loader {
|
|
|
// Found the subobject path in the preloaded file path map
|
|
|
fileName = scope.fileMap[ fileName ];
|
|
|
|
|
|
- } else {
|
|
|
+ } else {
|
|
|
|
|
|
// Standardized subfolders
|
|
|
if ( fileName.startsWith( 's/' ) ) {
|
|
@@ -1558,10 +1638,6 @@ class LDrawLoader extends Loader {
|
|
|
material: material,
|
|
|
matrix: matrix,
|
|
|
fileName: fileName,
|
|
|
- originalFileName: fileName,
|
|
|
- locationState: FILE_LOCATION_AS_IS,
|
|
|
- url: null,
|
|
|
- triedLowerCase: false,
|
|
|
inverted: bfcInverted !== currentParseScope.inverted,
|
|
|
startingConstructionStep: startingConstructionStep
|
|
|
} );
|
|
@@ -1730,7 +1806,7 @@ class LDrawLoader extends Loader {
|
|
|
|
|
|
if ( parsingEmbeddedFiles ) {
|
|
|
|
|
|
- this.subobjectCache[ currentEmbeddedFileName.toLowerCase() ] = currentEmbeddedText;
|
|
|
+ this.cache.setData( currentEmbeddedFileName.toLowerCase(), currentEmbeddedText );
|
|
|
|
|
|
}
|
|
|
|
|
@@ -1768,331 +1844,187 @@ class LDrawLoader extends Loader {
|
|
|
|
|
|
}
|
|
|
|
|
|
- processObject( text, onProcessed, subobject, url ) {
|
|
|
-
|
|
|
- const scope = this;
|
|
|
-
|
|
|
- const parseScope = scope.newParseScopeLevel();
|
|
|
- parseScope.url = url;
|
|
|
-
|
|
|
- const parentParseScope = scope.getParentParseScope();
|
|
|
-
|
|
|
- // Set current matrix
|
|
|
- if ( subobject ) {
|
|
|
-
|
|
|
- parseScope.currentMatrix.multiplyMatrices( parentParseScope.currentMatrix, subobject.matrix );
|
|
|
- parseScope.matrix.copy( subobject.matrix );
|
|
|
- parseScope.inverted = subobject.inverted;
|
|
|
- parseScope.startingConstructionStep = subobject.startingConstructionStep;
|
|
|
-
|
|
|
- }
|
|
|
+ finalizeObject( subobjectParseScope ) {
|
|
|
|
|
|
- // Add to cache
|
|
|
- let currentFileName = parentParseScope.currentFileName;
|
|
|
- if ( currentFileName !== null ) {
|
|
|
-
|
|
|
- currentFileName = parentParseScope.currentFileName.toLowerCase();
|
|
|
-
|
|
|
- }
|
|
|
+ const parentParseScope = subobjectParseScope.parentScope;
|
|
|
|
|
|
- if ( scope.subobjectCache[ currentFileName ] === undefined ) {
|
|
|
+ if ( this.smoothNormals && subobjectParseScope.type === 'Part' ) {
|
|
|
|
|
|
- scope.subobjectCache[ currentFileName ] = text;
|
|
|
+ smoothNormals( subobjectParseScope.faces, subobjectParseScope.lineSegments );
|
|
|
|
|
|
}
|
|
|
|
|
|
+ const isRoot = ! parentParseScope.isFromParse;
|
|
|
+ if ( this.separateObjects && ! isPrimitiveType( subobjectParseScope.type ) || isRoot ) {
|
|
|
|
|
|
- // Parse the object (returns a Group)
|
|
|
- scope.objectParse( text );
|
|
|
- let finishedCount = 0;
|
|
|
- onSubobjectFinish();
|
|
|
-
|
|
|
- function onSubobjectFinish() {
|
|
|
-
|
|
|
- finishedCount ++;
|
|
|
-
|
|
|
- if ( finishedCount === parseScope.subobjects.length + 1 ) {
|
|
|
-
|
|
|
- finalizeObject();
|
|
|
-
|
|
|
- } else {
|
|
|
+ const objGroup = subobjectParseScope.groupObject;
|
|
|
|
|
|
- // 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.
|
|
|
- const subobject = parseScope.subobjects[ parseScope.subobjectIndex ];
|
|
|
- Promise.resolve().then( function () {
|
|
|
+ if ( subobjectParseScope.faces.length > 0 ) {
|
|
|
|
|
|
- loadSubobject( subobject );
|
|
|
-
|
|
|
- } );
|
|
|
- parseScope.subobjectIndex ++;
|
|
|
+ objGroup.add( createObject( subobjectParseScope.faces, 3, false, subobjectParseScope.totalFaces ) );
|
|
|
|
|
|
}
|
|
|
|
|
|
- }
|
|
|
-
|
|
|
- function finalizeObject() {
|
|
|
-
|
|
|
- if ( scope.smoothNormals && parseScope.type === 'Part' ) {
|
|
|
+ if ( subobjectParseScope.lineSegments.length > 0 ) {
|
|
|
|
|
|
- smoothNormals( parseScope.faces, parseScope.lineSegments );
|
|
|
+ objGroup.add( createObject( subobjectParseScope.lineSegments, 2 ) );
|
|
|
|
|
|
}
|
|
|
|
|
|
- const isRoot = ! parentParseScope.isFromParse;
|
|
|
- if ( scope.separateObjects && ! isPrimitiveType( parseScope.type ) || isRoot ) {
|
|
|
-
|
|
|
- const objGroup = parseScope.groupObject;
|
|
|
-
|
|
|
- if ( parseScope.faces.length > 0 ) {
|
|
|
-
|
|
|
- objGroup.add( createObject( parseScope.faces, 3, false, parseScope.totalFaces ) );
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- if ( parseScope.lineSegments.length > 0 ) {
|
|
|
-
|
|
|
- objGroup.add( createObject( parseScope.lineSegments, 2 ) );
|
|
|
+ if ( subobjectParseScope.conditionalSegments.length > 0 ) {
|
|
|
|
|
|
- }
|
|
|
-
|
|
|
- if ( parseScope.conditionalSegments.length > 0 ) {
|
|
|
+ objGroup.add( createObject( subobjectParseScope.conditionalSegments, 2, true ) );
|
|
|
|
|
|
- objGroup.add( createObject( parseScope.conditionalSegments, 2, true ) );
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- if ( parentParseScope.groupObject ) {
|
|
|
+ }
|
|
|
|
|
|
- objGroup.name = parseScope.fileName;
|
|
|
- objGroup.userData.category = parseScope.category;
|
|
|
- objGroup.userData.keywords = parseScope.keywords;
|
|
|
- parseScope.matrix.decompose( objGroup.position, objGroup.quaternion, objGroup.scale );
|
|
|
+ if ( parentParseScope.groupObject ) {
|
|
|
|
|
|
- parentParseScope.groupObject.add( objGroup );
|
|
|
+ objGroup.name = subobjectParseScope.fileName;
|
|
|
+ objGroup.userData.category = subobjectParseScope.category;
|
|
|
+ objGroup.userData.keywords = subobjectParseScope.keywords;
|
|
|
+ subobjectParseScope.matrix.decompose( objGroup.position, objGroup.quaternion, objGroup.scale );
|
|
|
|
|
|
- }
|
|
|
+ parentParseScope.groupObject.add( objGroup );
|
|
|
|
|
|
- } else {
|
|
|
-
|
|
|
- const separateObjects = scope.separateObjects;
|
|
|
- const parentLineSegments = parentParseScope.lineSegments;
|
|
|
- const parentConditionalSegments = parentParseScope.conditionalSegments;
|
|
|
- const parentFaces = parentParseScope.faces;
|
|
|
+ }
|
|
|
|
|
|
- const lineSegments = parseScope.lineSegments;
|
|
|
- const conditionalSegments = parseScope.conditionalSegments;
|
|
|
- const faces = parseScope.faces;
|
|
|
+ } else {
|
|
|
|
|
|
- for ( let i = 0, l = lineSegments.length; i < l; i ++ ) {
|
|
|
+ const separateObjects = this.separateObjects;
|
|
|
+ const parentLineSegments = parentParseScope.lineSegments;
|
|
|
+ const parentConditionalSegments = parentParseScope.conditionalSegments;
|
|
|
+ const parentFaces = parentParseScope.faces;
|
|
|
|
|
|
- const ls = lineSegments[ i ];
|
|
|
+ const lineSegments = subobjectParseScope.lineSegments;
|
|
|
+ const conditionalSegments = subobjectParseScope.conditionalSegments;
|
|
|
+ const faces = subobjectParseScope.faces;
|
|
|
|
|
|
- if ( separateObjects ) {
|
|
|
+ for ( let i = 0, l = lineSegments.length; i < l; i ++ ) {
|
|
|
|
|
|
- const vertices = ls.vertices;
|
|
|
- vertices[ 0 ].applyMatrix4( parseScope.matrix );
|
|
|
- vertices[ 1 ].applyMatrix4( parseScope.matrix );
|
|
|
+ const ls = lineSegments[ i ];
|
|
|
|
|
|
- }
|
|
|
+ if ( separateObjects ) {
|
|
|
|
|
|
- parentLineSegments.push( ls );
|
|
|
+ const vertices = ls.vertices;
|
|
|
+ vertices[ 0 ].applyMatrix4( subobjectParseScope.matrix );
|
|
|
+ vertices[ 1 ].applyMatrix4( subobjectParseScope.matrix );
|
|
|
|
|
|
}
|
|
|
|
|
|
- for ( let i = 0, l = conditionalSegments.length; i < l; i ++ ) {
|
|
|
+ parentLineSegments.push( ls );
|
|
|
|
|
|
- const os = conditionalSegments[ i ];
|
|
|
+ }
|
|
|
|
|
|
- if ( separateObjects ) {
|
|
|
+ for ( let i = 0, l = conditionalSegments.length; i < l; i ++ ) {
|
|
|
|
|
|
- const vertices = os.vertices;
|
|
|
- const controlPoints = os.controlPoints;
|
|
|
- vertices[ 0 ].applyMatrix4( parseScope.matrix );
|
|
|
- vertices[ 1 ].applyMatrix4( parseScope.matrix );
|
|
|
- controlPoints[ 0 ].applyMatrix4( parseScope.matrix );
|
|
|
- controlPoints[ 1 ].applyMatrix4( parseScope.matrix );
|
|
|
+ const os = conditionalSegments[ i ];
|
|
|
|
|
|
- }
|
|
|
+ if ( separateObjects ) {
|
|
|
|
|
|
- parentConditionalSegments.push( os );
|
|
|
+ const vertices = os.vertices;
|
|
|
+ const controlPoints = os.controlPoints;
|
|
|
+ vertices[ 0 ].applyMatrix4( subobjectParseScope.matrix );
|
|
|
+ vertices[ 1 ].applyMatrix4( subobjectParseScope.matrix );
|
|
|
+ controlPoints[ 0 ].applyMatrix4( subobjectParseScope.matrix );
|
|
|
+ controlPoints[ 1 ].applyMatrix4( subobjectParseScope.matrix );
|
|
|
|
|
|
}
|
|
|
|
|
|
- for ( let i = 0, l = faces.length; i < l; i ++ ) {
|
|
|
+ parentConditionalSegments.push( os );
|
|
|
|
|
|
- const tri = faces[ i ];
|
|
|
+ }
|
|
|
|
|
|
- if ( separateObjects ) {
|
|
|
+ for ( let i = 0, l = faces.length; i < l; i ++ ) {
|
|
|
|
|
|
- const vertices = tri.vertices;
|
|
|
- for ( let i = 0, l = vertices.length; i < l; i ++ ) {
|
|
|
+ const tri = faces[ i ];
|
|
|
|
|
|
- vertices[ i ] = vertices[ i ].clone().applyMatrix4( parseScope.matrix );
|
|
|
+ if ( separateObjects ) {
|
|
|
|
|
|
- }
|
|
|
+ const vertices = tri.vertices;
|
|
|
+ for ( let i = 0, l = vertices.length; i < l; i ++ ) {
|
|
|
|
|
|
- _tempVec0.subVectors( vertices[ 1 ], vertices[ 0 ] );
|
|
|
- _tempVec1.subVectors( vertices[ 2 ], vertices[ 1 ] );
|
|
|
- tri.faceNormal.crossVectors( _tempVec0, _tempVec1 ).normalize();
|
|
|
+ vertices[ i ] = vertices[ i ].clone().applyMatrix4( subobjectParseScope.matrix );
|
|
|
|
|
|
}
|
|
|
|
|
|
- parentFaces.push( tri );
|
|
|
+ _tempVec0.subVectors( vertices[ 1 ], vertices[ 0 ] );
|
|
|
+ _tempVec1.subVectors( vertices[ 2 ], vertices[ 1 ] );
|
|
|
+ tri.faceNormal.crossVectors( _tempVec0, _tempVec1 ).normalize();
|
|
|
|
|
|
}
|
|
|
|
|
|
- parentParseScope.totalFaces += parseScope.totalFaces;
|
|
|
+ parentFaces.push( tri );
|
|
|
|
|
|
}
|
|
|
|
|
|
- scope.removeScopeLevel();
|
|
|
+ parentParseScope.totalFaces += subobjectParseScope.totalFaces;
|
|
|
|
|
|
- // If it is root object, compute construction steps
|
|
|
- if ( ! parentParseScope.isFromParse ) {
|
|
|
-
|
|
|
- scope.computeConstructionSteps( parseScope.groupObject );
|
|
|
+ }
|
|
|
|
|
|
- }
|
|
|
+ }
|
|
|
|
|
|
- if ( onProcessed ) {
|
|
|
+ async processObject( text, subobject, url, parentScope ) {
|
|
|
|
|
|
- onProcessed( parseScope.groupObject );
|
|
|
+ const scope = this;
|
|
|
|
|
|
- }
|
|
|
+ const parseScope = scope.newParseScopeLevel( null, parentScope );
|
|
|
+ parseScope.url = url;
|
|
|
|
|
|
- }
|
|
|
+ const parentParseScope = parseScope.parentScope;
|
|
|
|
|
|
- function loadSubobject( subobject ) {
|
|
|
+ // Set current matrix
|
|
|
+ if ( subobject ) {
|
|
|
|
|
|
+ parseScope.currentMatrix.multiplyMatrices( parentParseScope.currentMatrix, subobject.matrix );
|
|
|
+ parseScope.matrix.copy( subobject.matrix );
|
|
|
+ parseScope.inverted = subobject.inverted;
|
|
|
+ parseScope.startingConstructionStep = subobject.startingConstructionStep;
|
|
|
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
|
|
|
- const cached = scope.subobjectCache[ subobject.originalFileName.toLowerCase() ];
|
|
|
- if ( cached ) {
|
|
|
-
|
|
|
- scope.processObject( cached, function ( subobjectGroup ) {
|
|
|
-
|
|
|
- onSubobjectLoaded( subobjectGroup, subobject );
|
|
|
- onSubobjectFinish();
|
|
|
-
|
|
|
- }, subobject, url );
|
|
|
-
|
|
|
- return;
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- // 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.
|
|
|
- let subobjectURL = subobject.fileName;
|
|
|
- let newLocationState = FILE_LOCATION_NOT_FOUND;
|
|
|
-
|
|
|
- switch ( subobject.locationState ) {
|
|
|
-
|
|
|
- case FILE_LOCATION_AS_IS:
|
|
|
- newLocationState = subobject.locationState + 1;
|
|
|
- break;
|
|
|
-
|
|
|
- case FILE_LOCATION_TRY_PARTS:
|
|
|
- subobjectURL = 'parts/' + subobjectURL;
|
|
|
- newLocationState = subobject.locationState + 1;
|
|
|
- break;
|
|
|
-
|
|
|
- case FILE_LOCATION_TRY_P:
|
|
|
- subobjectURL = 'p/' + subobjectURL;
|
|
|
- newLocationState = subobject.locationState + 1;
|
|
|
- break;
|
|
|
-
|
|
|
- case FILE_LOCATION_TRY_MODELS:
|
|
|
- subobjectURL = 'models/' + subobjectURL;
|
|
|
- newLocationState = subobject.locationState + 1;
|
|
|
- break;
|
|
|
|
|
|
- case FILE_LOCATION_TRY_RELATIVE:
|
|
|
- subobjectURL = url.substring( 0, url.lastIndexOf( '/' ) + 1 ) + subobjectURL;
|
|
|
- newLocationState = subobject.locationState + 1;
|
|
|
- break;
|
|
|
-
|
|
|
- case FILE_LOCATION_TRY_ABSOLUTE:
|
|
|
-
|
|
|
- if ( subobject.triedLowerCase ) {
|
|
|
-
|
|
|
- // Try absolute path
|
|
|
- newLocationState = FILE_LOCATION_NOT_FOUND;
|
|
|
-
|
|
|
- } else {
|
|
|
-
|
|
|
- // Next attempt is lower case
|
|
|
- subobject.fileName = subobject.fileName.toLowerCase();
|
|
|
- subobjectURL = subobject.fileName;
|
|
|
- subobject.triedLowerCase = true;
|
|
|
- newLocationState = FILE_LOCATION_AS_IS;
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- break;
|
|
|
-
|
|
|
- case FILE_LOCATION_NOT_FOUND:
|
|
|
-
|
|
|
- // All location possibilities have been tried, give up loading this object
|
|
|
- console.warn( 'LDrawLoader: Subobject "' + subobject.originalFileName + '" could not be found.' );
|
|
|
-
|
|
|
- return;
|
|
|
+ }
|
|
|
|
|
|
- }
|
|
|
+ // Parse the object
|
|
|
+ this.objectParse( text, parseScope );
|
|
|
|
|
|
- subobject.locationState = newLocationState;
|
|
|
- subobject.url = subobjectURL;
|
|
|
+ const subobjects = parseScope.subobjects;
|
|
|
+ const promises = [];
|
|
|
+ for ( let i = 0, l = subobjects.length; i < l; i ++ ) {
|
|
|
|
|
|
- // 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.
|
|
|
- const fileLoader = new FileLoader( scope.manager );
|
|
|
- fileLoader.setPath( scope.partsLibraryPath );
|
|
|
- fileLoader.setRequestHeader( scope.requestHeader );
|
|
|
- fileLoader.setWithCredentials( scope.withCredentials );
|
|
|
- fileLoader.load( subobjectURL, function ( text ) {
|
|
|
+ promises.push( loadSubobject( parseScope.subobjects[ i ] ) );
|
|
|
|
|
|
- scope.processObject( text, function ( subobjectGroup ) {
|
|
|
+ }
|
|
|
|
|
|
- onSubobjectLoaded( subobjectGroup, subobject );
|
|
|
- onSubobjectFinish();
|
|
|
+ // Kick off of the downloads in parallel but process all the subobjects
|
|
|
+ // in order so all the assembly instructions are correct
|
|
|
+ const subobjectScopes = await Promise.all( promises );
|
|
|
+ for ( let i = 0, l = subobjectScopes.length; i < l; i ++ ) {
|
|
|
|
|
|
- }, subobject, url );
|
|
|
+ this.finalizeObject( subobjectScopes[ i ] );
|
|
|
|
|
|
- }, undefined, function ( err ) {
|
|
|
+ }
|
|
|
|
|
|
- onSubobjectError( err, subobject );
|
|
|
+ // If it is root object then finalize this object and compute construction steps
|
|
|
+ if ( ! parentParseScope.isFromParse ) {
|
|
|
|
|
|
- }, subobject );
|
|
|
+ this.finalizeObject( parseScope );
|
|
|
+ this.computeConstructionSteps( parseScope.groupObject );
|
|
|
|
|
|
}
|
|
|
|
|
|
- function onSubobjectLoaded( subobjectGroup, subobject ) {
|
|
|
+ return parseScope;
|
|
|
|
|
|
- if ( subobjectGroup === null ) {
|
|
|
-
|
|
|
- // Try to reload
|
|
|
- loadSubobject( subobject );
|
|
|
- return;
|
|
|
+ function loadSubobject( subobject ) {
|
|
|
|
|
|
- }
|
|
|
+ return scope.cache.loadData( subobject.fileName ).then( function ( text ) {
|
|
|
|
|
|
- scope.fileMap[ subobject.originalFileName ] = subobject.url;
|
|
|
+ return scope.processObject( text, subobject, url, parseScope );
|
|
|
|
|
|
- }
|
|
|
+ } ).catch( function () {
|
|
|
|
|
|
- function onSubobjectError( err, subobject ) {
|
|
|
+ console.warn( 'LDrawLoader: Subobject "' + subobject.fileName + '" could not be found.' );
|
|
|
|
|
|
- // Retry download from a different default possible location
|
|
|
- loadSubobject( subobject );
|
|
|
+ } );
|
|
|
|
|
|
}
|
|
|
|