super-three 0.136.1 → 0.137.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (100) hide show
  1. package/README.md +1 -1
  2. package/build/three.cjs +37132 -0
  3. package/build/three.js +71 -53
  4. package/build/three.min.js +2 -2
  5. package/build/three.module.js +80 -47
  6. package/examples/js/animation/MMDAnimationHelper.js +3 -1
  7. package/examples/js/controls/experimental/CameraControls.js +6 -2
  8. package/examples/js/exporters/GLTFExporter.js +82 -39
  9. package/examples/js/geometries/TextGeometry.js +12 -10
  10. package/examples/js/interactive/HTMLMesh.js +14 -2
  11. package/examples/js/loaders/EXRLoader.js +20 -9
  12. package/examples/js/loaders/GLTFLoader.js +1 -1
  13. package/examples/js/loaders/LDrawLoader.js +541 -425
  14. package/examples/js/loaders/MD2Loader.js +5 -5
  15. package/examples/js/loaders/MTLLoader.js +10 -3
  16. package/examples/js/loaders/PCDLoader.js +2 -2
  17. package/examples/js/loaders/STLLoader.js +1 -1
  18. package/examples/js/loaders/SVGLoader.js +3 -2
  19. package/examples/js/modifiers/EdgeSplitModifier.js +0 -6
  20. package/examples/js/modifiers/SimplifyModifier.js +0 -10
  21. package/examples/js/objects/Lensflare.js +2 -2
  22. package/examples/js/postprocessing/OutlinePass.js +1 -5
  23. package/examples/js/renderers/CSS2DRenderer.js +25 -19
  24. package/examples/js/renderers/CSS3DRenderer.js +37 -32
  25. package/examples/js/utils/LDrawUtils.js +182 -0
  26. package/examples/jsm/exporters/GLTFExporter.js +80 -12
  27. package/examples/jsm/geometries/TextGeometry.js +13 -13
  28. package/examples/jsm/interactive/HTMLMesh.js +16 -2
  29. package/examples/jsm/libs/flow.module.js +930 -169
  30. package/examples/jsm/loaders/EXRLoader.js +19 -9
  31. package/examples/jsm/loaders/GLTFLoader.js +1 -1
  32. package/examples/jsm/loaders/LDrawLoader.js +534 -442
  33. package/examples/jsm/loaders/MD2Loader.js +5 -5
  34. package/examples/jsm/loaders/MTLLoader.js +11 -4
  35. package/examples/jsm/loaders/PCDLoader.js +2 -2
  36. package/examples/jsm/loaders/STLLoader.js +1 -1
  37. package/examples/jsm/loaders/SVGLoader.js +3 -1
  38. package/examples/jsm/loaders/VRMLLoader.js +0 -1
  39. package/examples/jsm/modifiers/EdgeSplitModifier.js +0 -6
  40. package/examples/jsm/modifiers/SimplifyModifier.js +0 -10
  41. package/examples/jsm/node-editor/NodeEditor.js +435 -181
  42. package/examples/jsm/node-editor/accessors/NormalEditor.js +8 -8
  43. package/examples/jsm/node-editor/accessors/PositionEditor.js +8 -8
  44. package/examples/jsm/node-editor/accessors/UVEditor.js +6 -7
  45. package/examples/jsm/node-editor/core/BaseNode.js +83 -0
  46. package/examples/jsm/node-editor/display/BlendEditor.js +7 -6
  47. package/examples/jsm/node-editor/examples/animate-uv.json +1 -1
  48. package/examples/jsm/node-editor/examples/fake-top-light.json +1 -1
  49. package/examples/jsm/node-editor/examples/oscillator-color.json +1 -1
  50. package/examples/jsm/node-editor/examples/rim.json +1 -1
  51. package/examples/jsm/node-editor/inputs/ColorEditor.js +14 -10
  52. package/examples/jsm/node-editor/inputs/FloatEditor.js +6 -7
  53. package/examples/jsm/node-editor/inputs/SliderEditor.js +4 -5
  54. package/examples/jsm/node-editor/inputs/Vector2Editor.js +6 -7
  55. package/examples/jsm/node-editor/inputs/Vector3Editor.js +8 -9
  56. package/examples/jsm/node-editor/inputs/Vector4Editor.js +9 -10
  57. package/examples/jsm/node-editor/materials/BasicMaterialEditor.js +87 -0
  58. package/examples/jsm/node-editor/materials/PointsMaterialEditor.js +97 -0
  59. package/examples/jsm/node-editor/materials/StandardMaterialEditor.js +20 -17
  60. package/examples/jsm/node-editor/math/AngleEditor.js +39 -0
  61. package/examples/jsm/node-editor/math/DotEditor.js +6 -7
  62. package/examples/jsm/node-editor/math/InvertEditor.js +4 -3
  63. package/examples/jsm/node-editor/math/LimiterEditor.js +11 -10
  64. package/examples/jsm/node-editor/math/NormalizeEditor.js +6 -5
  65. package/examples/jsm/node-editor/math/OperatorEditor.js +12 -11
  66. package/examples/jsm/node-editor/math/PowerEditor.js +6 -5
  67. package/examples/jsm/node-editor/math/TrigonometryEditor.js +12 -7
  68. package/examples/jsm/node-editor/procedural/CheckerEditor.js +4 -3
  69. package/examples/jsm/node-editor/scene/MeshEditor.js +99 -0
  70. package/examples/jsm/node-editor/scene/Object3DEditor.js +160 -0
  71. package/examples/jsm/node-editor/scene/PointsEditor.js +99 -0
  72. package/examples/jsm/node-editor/utils/JoinEditor.js +58 -0
  73. package/examples/jsm/node-editor/utils/OscillatorEditor.js +7 -6
  74. package/examples/jsm/node-editor/utils/SplitEditor.js +39 -0
  75. package/examples/jsm/node-editor/utils/TimerEditor.js +6 -5
  76. package/examples/jsm/postprocessing/OutlinePass.js +1 -4
  77. package/examples/jsm/renderers/CSS2DRenderer.js +24 -19
  78. package/examples/jsm/renderers/CSS3DRenderer.js +36 -30
  79. package/examples/jsm/renderers/nodes/accessors/UVNode.js +4 -4
  80. package/examples/jsm/renderers/nodes/core/NodeBuilder.js +2 -1
  81. package/examples/jsm/renderers/nodes/utils/SplitNode.js +19 -4
  82. package/examples/jsm/utils/LDrawUtils.js +18 -11
  83. package/package.json +16 -9
  84. package/src/constants.js +1 -3
  85. package/src/core/BufferGeometry.js +2 -2
  86. package/src/geometries/LatheGeometry.js +1 -1
  87. package/src/loaders/FileLoader.js +20 -3
  88. package/src/math/Box3.js +24 -10
  89. package/src/renderers/WebGLRenderTarget.js +4 -1
  90. package/src/renderers/WebGLRenderer.js +3 -3
  91. package/src/renderers/shaders/ShaderChunk/morphnormal_vertex.glsl.js +1 -1
  92. package/src/renderers/shaders/ShaderChunk/morphtarget_vertex.glsl.js +2 -2
  93. package/src/renderers/webgl/WebGLBackground.js +2 -2
  94. package/src/renderers/webgl/WebGLGeometries.js +2 -2
  95. package/src/renderers/webgl/WebGLPrograms.js +2 -2
  96. package/src/renderers/webgl/WebGLState.js +2 -2
  97. package/src/renderers/webgl/WebGLTextures.js +9 -6
  98. package/src/renderers/webgl/WebGLUtils.js +1 -3
  99. package/src/renderers/webxr/WebXRManager.js +2 -0
  100. package/src/utils.js +15 -1
@@ -7,7 +7,6 @@ import {
7
7
  LineBasicMaterial,
8
8
  LineSegments,
9
9
  Loader,
10
- Matrix3,
11
10
  Matrix4,
12
11
  Mesh,
13
12
  MeshStandardMaterial,
@@ -37,6 +36,9 @@ const FILE_LOCATION_TRY_RELATIVE = 4;
37
36
  const FILE_LOCATION_TRY_ABSOLUTE = 5;
38
37
  const FILE_LOCATION_NOT_FOUND = 6;
39
38
 
39
+ const MAIN_COLOUR_CODE = '16';
40
+ const MAIN_EDGE_COLOUR_CODE = '24';
41
+
40
42
  const _tempVec0 = new Vector3();
41
43
  const _tempVec1 = new Vector3();
42
44
 
@@ -169,6 +171,17 @@ class LDrawConditionalLineMaterial extends ShaderMaterial {
169
171
 
170
172
  }
171
173
 
174
+ class ConditionalLineSegments extends LineSegments {
175
+
176
+ constructor( geometry, material ) {
177
+
178
+ super( geometry, material );
179
+ this.isConditionalLine = true;
180
+
181
+ }
182
+
183
+ }
184
+
172
185
  function generateFaceNormals( faces ) {
173
186
 
174
187
  for ( let i = 0, l = faces.length; i < l; i ++ ) {
@@ -523,13 +536,7 @@ function smoothNormals( faces, lineSegments, checkSubSegments = false ) {
523
536
 
524
537
  function isPartType( type ) {
525
538
 
526
- return type === 'Part';
527
-
528
- }
529
-
530
- function isModelType( type ) {
531
-
532
- return type === 'Model' || type === 'Unofficial_Model';
539
+ return type === 'Part' || type === 'Unofficial_Part';
533
540
 
534
541
  }
535
542
 
@@ -628,12 +635,13 @@ class LineParser {
628
635
 
629
636
  }
630
637
 
638
+ // Fetches and parses an intermediate representation of LDraw parts files.
631
639
  class LDrawParsedCache {
632
640
 
633
641
  constructor( loader ) {
634
642
 
635
643
  this.loader = loader;
636
- this.cache = {};
644
+ this._cache = {};
637
645
 
638
646
  }
639
647
 
@@ -684,6 +692,7 @@ class LDrawParsedCache {
684
692
  result.totalFaces = original.totalFaces;
685
693
  result.startingConstructionStep = original.startingConstructionStep;
686
694
  result.materials = original.materials;
695
+ result.group = null;
687
696
  return result;
688
697
 
689
698
  }
@@ -765,7 +774,7 @@ class LDrawParsedCache {
765
774
 
766
775
  }
767
776
 
768
- parse( text ) {
777
+ parse( text, fileName = null ) {
769
778
 
770
779
  const loader = this.loader;
771
780
 
@@ -778,7 +787,7 @@ class LDrawParsedCache {
778
787
 
779
788
  const getLocalMaterial = colorCode => {
780
789
 
781
- return colorCode in materials ? materials[ colorCode ] : null;
790
+ return materials[ colorCode ] || null;
782
791
 
783
792
  };
784
793
 
@@ -1215,36 +1224,380 @@ class LDrawParsedCache {
1215
1224
  subobjects,
1216
1225
  totalFaces,
1217
1226
  startingConstructionStep,
1218
- materials
1227
+ materials,
1228
+ fileName,
1229
+ group: null
1219
1230
  };
1220
1231
 
1221
1232
  }
1222
1233
 
1223
- loadData( fileName ) {
1234
+ // returns an (optionally cloned) instance of the data
1235
+ getData( fileName, clone = true ) {
1224
1236
 
1225
1237
  const key = fileName.toLowerCase();
1226
- if ( ! ( key in this.cache ) ) {
1238
+ const result = this._cache[ key ];
1239
+ if ( result === null || result instanceof Promise ) {
1227
1240
 
1228
- this.cache[ key ] = this.fetchData( fileName ).then( text => {
1241
+ return null;
1229
1242
 
1230
- return this.parse( text );
1243
+ }
1231
1244
 
1232
- } );
1245
+ if ( clone ) {
1246
+
1247
+ return this.cloneResult( result );
1248
+
1249
+ } else {
1250
+
1251
+ return result;
1233
1252
 
1234
1253
  }
1235
1254
 
1236
- return this.cache[ key ].then( result => {
1255
+ }
1237
1256
 
1238
- return this.cloneResult( result );
1257
+ // kicks off a fetch and parse of the requested data if it hasn't already been loaded. Returns when
1258
+ // the data is ready to use and can be retrieved synchronously with "getData".
1259
+ async ensureDataLoaded( fileName ) {
1239
1260
 
1240
- } );
1261
+ const key = fileName.toLowerCase();
1262
+ if ( ! ( key in this._cache ) ) {
1263
+
1264
+ // replace the promise with a copy of the parsed data for immediate processing
1265
+ this._cache[ key ] = this.fetchData( fileName ).then( text => {
1266
+
1267
+ const info = this.parse( text, fileName );
1268
+ this._cache[ key ] = info;
1269
+ return info;
1270
+
1271
+ } );
1272
+
1273
+ }
1274
+
1275
+ await this._cache[ key ];
1241
1276
 
1242
1277
  }
1243
1278
 
1279
+ // sets the data in the cache from parsed data
1244
1280
  setData( fileName, text ) {
1245
1281
 
1246
1282
  const key = fileName.toLowerCase();
1247
- this.cache[ key ] = Promise.resolve( this.parse( text ) );
1283
+ this._cache[ key ] = this.parse( text, fileName );
1284
+
1285
+ }
1286
+
1287
+ }
1288
+
1289
+ // returns the material for an associated color code. If the color code is 16 for a face or 24 for
1290
+ // an edge then the passthroughColorCode is used.
1291
+ function getMaterialFromCode( colorCode, parentColorCode, materialHierarchy, forEdge ) {
1292
+
1293
+ const isPassthrough = ! forEdge && colorCode === MAIN_COLOUR_CODE || forEdge && colorCode === MAIN_EDGE_COLOUR_CODE;
1294
+ if ( isPassthrough ) {
1295
+
1296
+ colorCode = parentColorCode;
1297
+
1298
+ }
1299
+
1300
+ return materialHierarchy[ colorCode ] || null;
1301
+
1302
+ }
1303
+
1304
+ // Class used to parse and build LDraw parts as three.js objects and cache them if they're a "Part" type.
1305
+ class LDrawPartsGeometryCache {
1306
+
1307
+ constructor( loader ) {
1308
+
1309
+ this.loader = loader;
1310
+ this.parseCache = new LDrawParsedCache( loader );
1311
+ this._cache = {};
1312
+
1313
+ }
1314
+
1315
+ // Convert the given file information into a mesh by processing subobjects.
1316
+ async processIntoMesh( info ) {
1317
+
1318
+ const loader = this.loader;
1319
+ const parseCache = this.parseCache;
1320
+ const faceMaterials = new Set();
1321
+
1322
+ // Processes the part subobject information to load child parts and merge geometry onto part
1323
+ // piece object.
1324
+ const processInfoSubobjects = async ( info, subobject = null ) => {
1325
+
1326
+ const subobjects = info.subobjects;
1327
+ const promises = [];
1328
+
1329
+ // Trigger load of all subobjects. If a subobject isn't a primitive then load it as a separate
1330
+ // group which lets instruction steps apply correctly.
1331
+ for ( let i = 0, l = subobjects.length; i < l; i ++ ) {
1332
+
1333
+ const subobject = subobjects[ i ];
1334
+ const promise = parseCache.ensureDataLoaded( subobject.fileName ).then( () => {
1335
+
1336
+ const subobjectInfo = parseCache.getData( subobject.fileName, false );
1337
+ if ( ! isPrimitiveType( subobjectInfo.type ) ) {
1338
+
1339
+ return this.loadModel( subobject.fileName ).catch( error => {
1340
+
1341
+ console.warn( error );
1342
+ return null;
1343
+
1344
+ } );
1345
+
1346
+ }
1347
+
1348
+ return processInfoSubobjects( parseCache.getData( subobject.fileName ), subobject );
1349
+
1350
+ } );
1351
+
1352
+ promises.push( promise );
1353
+
1354
+ }
1355
+
1356
+ const group = new Group();
1357
+ group.userData.category = info.category;
1358
+ group.userData.keywords = info.keywords;
1359
+ info.group = group;
1360
+
1361
+ const subobjectInfos = await Promise.all( promises );
1362
+ for ( let i = 0, l = subobjectInfos.length; i < l; i ++ ) {
1363
+
1364
+ const subobject = info.subobjects[ i ];
1365
+ const subobjectInfo = subobjectInfos[ i ];
1366
+
1367
+ if ( subobjectInfo === null ) {
1368
+
1369
+ // the subobject failed to load
1370
+ continue;
1371
+
1372
+ }
1373
+
1374
+ // if the subobject was loaded as a separate group then apply the parent scopes materials
1375
+ if ( subobjectInfo.isGroup ) {
1376
+
1377
+ const subobjectGroup = subobjectInfo;
1378
+ subobject.matrix.decompose( subobjectGroup.position, subobjectGroup.quaternion, subobjectGroup.scale );
1379
+ subobjectGroup.userData.startingConstructionStep = subobject.startingConstructionStep;
1380
+ subobjectGroup.name = subobject.fileName;
1381
+
1382
+ loader.applyMaterialsToMesh( subobjectGroup, subobject.colorCode, info.materials );
1383
+
1384
+ group.add( subobjectGroup );
1385
+ continue;
1386
+
1387
+ }
1388
+
1389
+ // add the subobject group if it has children in case it has both children and primitives
1390
+ if ( subobjectInfo.group.children.length ) {
1391
+
1392
+ group.add( subobjectInfo.group );
1393
+
1394
+ }
1395
+
1396
+ // transform the primitives into the local space of the parent piece and append them to
1397
+ // to the parent primitives list.
1398
+ const parentLineSegments = info.lineSegments;
1399
+ const parentConditionalSegments = info.conditionalSegments;
1400
+ const parentFaces = info.faces;
1401
+
1402
+ const lineSegments = subobjectInfo.lineSegments;
1403
+ const conditionalSegments = subobjectInfo.conditionalSegments;
1404
+
1405
+ const faces = subobjectInfo.faces;
1406
+ const matrix = subobject.matrix;
1407
+ const inverted = subobject.inverted;
1408
+ const matrixScaleInverted = matrix.determinant() < 0;
1409
+ const colorCode = subobject.colorCode;
1410
+
1411
+ const lineColorCode = colorCode === MAIN_COLOUR_CODE ? MAIN_EDGE_COLOUR_CODE : colorCode;
1412
+ for ( let i = 0, l = lineSegments.length; i < l; i ++ ) {
1413
+
1414
+ const ls = lineSegments[ i ];
1415
+ const vertices = ls.vertices;
1416
+ vertices[ 0 ].applyMatrix4( matrix );
1417
+ vertices[ 1 ].applyMatrix4( matrix );
1418
+ ls.colorCode = ls.colorCode === MAIN_EDGE_COLOUR_CODE ? lineColorCode : ls.colorCode;
1419
+ ls.material = ls.material || getMaterialFromCode( ls.colorCode, ls.colorCode, info.materials, true );
1420
+
1421
+ parentLineSegments.push( ls );
1422
+
1423
+ }
1424
+
1425
+ for ( let i = 0, l = conditionalSegments.length; i < l; i ++ ) {
1426
+
1427
+ const os = conditionalSegments[ i ];
1428
+ const vertices = os.vertices;
1429
+ const controlPoints = os.controlPoints;
1430
+ vertices[ 0 ].applyMatrix4( matrix );
1431
+ vertices[ 1 ].applyMatrix4( matrix );
1432
+ controlPoints[ 0 ].applyMatrix4( matrix );
1433
+ controlPoints[ 1 ].applyMatrix4( matrix );
1434
+ os.colorCode = os.colorCode === MAIN_EDGE_COLOUR_CODE ? lineColorCode : os.colorCode;
1435
+ os.material = os.material || getMaterialFromCode( os.colorCode, os.colorCode, info.materials, true );
1436
+
1437
+ parentConditionalSegments.push( os );
1438
+
1439
+ }
1440
+
1441
+ for ( let i = 0, l = faces.length; i < l; i ++ ) {
1442
+
1443
+ const tri = faces[ i ];
1444
+ const vertices = tri.vertices;
1445
+ for ( let i = 0, l = vertices.length; i < l; i ++ ) {
1446
+
1447
+ vertices[ i ].applyMatrix4( matrix );
1448
+
1449
+ }
1450
+
1451
+ tri.colorCode = tri.colorCode === MAIN_COLOUR_CODE ? colorCode : tri.colorCode;
1452
+ tri.material = tri.material || getMaterialFromCode( tri.colorCode, colorCode, info.materials, false );
1453
+ faceMaterials.add( tri.colorCode );
1454
+
1455
+ // If the scale of the object is negated then the triangle winding order
1456
+ // needs to be flipped.
1457
+ if ( matrixScaleInverted !== inverted ) {
1458
+
1459
+ vertices.reverse();
1460
+
1461
+ }
1462
+
1463
+ parentFaces.push( tri );
1464
+
1465
+ }
1466
+
1467
+ info.totalFaces += subobjectInfo.totalFaces;
1468
+
1469
+ }
1470
+
1471
+ // Apply the parent subobjects pass through material code to this object. This is done several times due
1472
+ // to material scoping.
1473
+ if ( subobject ) {
1474
+
1475
+ loader.applyMaterialsToMesh( group, subobject.colorCode, info.materials );
1476
+
1477
+ }
1478
+
1479
+ return info;
1480
+
1481
+ };
1482
+
1483
+ // Track material use to see if we need to use the normal smooth slow path for hard edges.
1484
+ for ( let i = 0, l = info.faces; i < l; i ++ ) {
1485
+
1486
+ faceMaterials.add( info.faces[ i ].colorCode );
1487
+
1488
+ }
1489
+
1490
+ await processInfoSubobjects( info );
1491
+
1492
+ if ( loader.smoothNormals ) {
1493
+
1494
+ const checkSubSegments = faceMaterials.size > 1;
1495
+ generateFaceNormals( info.faces );
1496
+ smoothNormals( info.faces, info.lineSegments, checkSubSegments );
1497
+
1498
+ }
1499
+
1500
+ // Add the primitive objects and metadata.
1501
+ const group = info.group;
1502
+ if ( info.faces.length > 0 ) {
1503
+
1504
+ group.add( createObject( info.faces, 3, false, info.totalFaces ) );
1505
+
1506
+ }
1507
+
1508
+ if ( info.lineSegments.length > 0 ) {
1509
+
1510
+ group.add( createObject( info.lineSegments, 2 ) );
1511
+
1512
+ }
1513
+
1514
+ if ( info.conditionalSegments.length > 0 ) {
1515
+
1516
+ group.add( createObject( info.conditionalSegments, 2, true ) );
1517
+
1518
+ }
1519
+
1520
+ return group;
1521
+
1522
+ }
1523
+
1524
+ hasCachedModel( fileName ) {
1525
+
1526
+ return fileName !== null && fileName.toLowerCase() in this._cache;
1527
+
1528
+ }
1529
+
1530
+ async getCachedModel( fileName ) {
1531
+
1532
+ if ( fileName !== null && this.hasCachedModel( fileName ) ) {
1533
+
1534
+ const key = fileName.toLowerCase();
1535
+ const group = await this._cache[ key ];
1536
+ return group.clone();
1537
+
1538
+ } else {
1539
+
1540
+ return null;
1541
+
1542
+ }
1543
+
1544
+ }
1545
+
1546
+ // Loads and parses the model with the given file name. Returns a cached copy if available.
1547
+ async loadModel( fileName ) {
1548
+
1549
+ const parseCache = this.parseCache;
1550
+ const key = fileName.toLowerCase();
1551
+ if ( this.hasCachedModel( fileName ) ) {
1552
+
1553
+ // Return cached model if available.
1554
+ return this.getCachedModel( fileName );
1555
+
1556
+ } else {
1557
+
1558
+ // Otherwise parse a new model.
1559
+ // Ensure the file data is loaded and pre parsed.
1560
+ await parseCache.ensureDataLoaded( fileName );
1561
+
1562
+ const info = parseCache.getData( fileName );
1563
+ const promise = this.processIntoMesh( info );
1564
+
1565
+ // Now that the file has loaded it's possible that another part parse has been waiting in parallel
1566
+ // so check the cache again to see if it's been added since the last async operation so we don't
1567
+ // do unnecessary work.
1568
+ if ( this.hasCachedModel( fileName ) ) {
1569
+
1570
+ return this.getCachedModel( fileName );
1571
+
1572
+ }
1573
+
1574
+ // Cache object if it's a part so it can be reused later.
1575
+ if ( isPartType( info.type ) ) {
1576
+
1577
+ this._cache[ key ] = promise;
1578
+
1579
+ }
1580
+
1581
+ // return a copy
1582
+ const group = await promise;
1583
+ return group.clone();
1584
+
1585
+ }
1586
+
1587
+ }
1588
+
1589
+ // parses the given model text into a renderable object. Returns cached copy if available.
1590
+ async parseModel( text ) {
1591
+
1592
+ const parseCache = this.parseCache;
1593
+ const info = parseCache.parse( text );
1594
+ if ( isPartType( info.type ) && this.hasCachedModel( info.fileName ) ) {
1595
+
1596
+ return this.getCachedModel( info.fileName );
1597
+
1598
+ }
1599
+
1600
+ return this.processIntoMesh( info );
1248
1601
 
1249
1602
  }
1250
1603
 
@@ -1367,7 +1720,7 @@ function createObject( elements, elementSize, isConditionalSegments = false, tot
1367
1720
 
1368
1721
  }
1369
1722
 
1370
- if ( prevMaterial !== elem.material ) {
1723
+ if ( prevMaterial !== elem.colorCode ) {
1371
1724
 
1372
1725
  if ( prevMaterial !== null ) {
1373
1726
 
@@ -1376,25 +1729,43 @@ function createObject( elements, elementSize, isConditionalSegments = false, tot
1376
1729
  }
1377
1730
 
1378
1731
  const material = elem.material;
1379
- if ( elementSize === 3 ) {
1732
+ if ( material !== null ) {
1380
1733
 
1381
- materials.push( material );
1734
+ if ( elementSize === 3 ) {
1382
1735
 
1383
- } else if ( elementSize === 2 ) {
1736
+ materials.push( material );
1384
1737
 
1385
- if ( isConditionalSegments ) {
1738
+ } else if ( elementSize === 2 ) {
1386
1739
 
1387
- materials.push( material.userData.edgeMaterial.userData.conditionalEdgeMaterial );
1740
+ if ( material !== null ) {
1388
1741
 
1389
- } else {
1742
+ if ( isConditionalSegments ) {
1743
+
1744
+ materials.push( material.userData.edgeMaterial.userData.conditionalEdgeMaterial );
1390
1745
 
1391
- materials.push( material.userData.edgeMaterial );
1746
+ } else {
1747
+
1748
+ materials.push( material.userData.edgeMaterial );
1749
+
1750
+ }
1751
+
1752
+ } else {
1753
+
1754
+ materials.push( null );
1755
+
1756
+ }
1392
1757
 
1393
1758
  }
1394
1759
 
1760
+ } else {
1761
+
1762
+ // If a material has not been made available yet then keep the color code string in the material array
1763
+ // to save the spot for the material once a parent scopes materials are being applied to the object.
1764
+ materials.push( elem.colorCode );
1765
+
1395
1766
  }
1396
1767
 
1397
- prevMaterial = elem.material;
1768
+ prevMaterial = elem.colorCode;
1398
1769
  index0 = offset / 3;
1399
1770
  numGroupVerts = vertices.length;
1400
1771
 
@@ -1426,7 +1797,15 @@ function createObject( elements, elementSize, isConditionalSegments = false, tot
1426
1797
 
1427
1798
  if ( elementSize === 2 ) {
1428
1799
 
1429
- object3d = new LineSegments( bufferGeometry, materials.length === 1 ? materials[ 0 ] : materials );
1800
+ if ( isConditionalSegments ) {
1801
+
1802
+ object3d = new ConditionalLineSegments( bufferGeometry, materials.length === 1 ? materials[ 0 ] : materials );
1803
+
1804
+ } else {
1805
+
1806
+ object3d = new LineSegments( bufferGeometry, materials.length === 1 ? materials[ 0 ] : materials );
1807
+
1808
+ }
1430
1809
 
1431
1810
  } else if ( elementSize === 3 ) {
1432
1811
 
@@ -1486,9 +1865,6 @@ function createObject( elements, elementSize, isConditionalSegments = false, tot
1486
1865
 
1487
1866
  //
1488
1867
 
1489
- const MAIN_COLOUR_CODE = '16';
1490
- const MAIN_EDGE_COLOUR_CODE = '24';
1491
-
1492
1868
  class LDrawLoader extends Loader {
1493
1869
 
1494
1870
  constructor( manager ) {
@@ -1497,21 +1873,16 @@ class LDrawLoader extends Loader {
1497
1873
 
1498
1874
  // Array of THREE.Material
1499
1875
  this.materials = [];
1876
+ this.materialLibrary = {};
1500
1877
 
1501
- // Not using THREE.Cache here because it returns the previous HTML error response instead of calling onError()
1502
1878
  // This also allows to handle the embedded text files ("0 FILE" lines)
1503
- this.parseCache = new LDrawParsedCache( this );
1879
+ this.partsCache = new LDrawPartsGeometryCache( this );
1504
1880
 
1505
1881
  // 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.
1506
1882
  this.fileMap = {};
1507
1883
 
1508
- this.rootParseScope = this.newParseScopeLevel();
1509
-
1510
- // Add default main triangle and line edge materials (used in pieces that can be colored with a main color)
1511
- this.setMaterials( [
1512
- this.parseColorMetaDirective( new LineParser( 'Main_Colour CODE 16 VALUE #FF8080 EDGE #333333' ) ),
1513
- this.parseColorMetaDirective( new LineParser( 'Edge_Colour CODE 24 VALUE #A0A0A0 EDGE #333333' ) )
1514
- ] );
1884
+ // Initializes the materials library with default materials
1885
+ this.setMaterials( [] );
1515
1886
 
1516
1887
  // If this flag is set to true the vertex normals will be smoothed.
1517
1888
  this.smoothNormals = true;
@@ -1564,26 +1935,29 @@ class LDrawLoader extends Loader {
1564
1935
  fileLoader.setWithCredentials( this.withCredentials );
1565
1936
  fileLoader.load( url, text => {
1566
1937
 
1567
- const parsedInfo = this.parseCache.parse( text );
1568
- this.processObject( parsedInfo, null, url, this.rootParseScope )
1569
- .then( function ( result ) {
1938
+ this.partsCache
1939
+ .parseModel( text, this.materialLibrary )
1940
+ .then( group => {
1570
1941
 
1571
- onLoad( result.groupObject );
1942
+ this.applyMaterialsToMesh( group, MAIN_COLOUR_CODE, this.materialLibrary, true );
1943
+ this.computeConstructionSteps( group );
1944
+ onLoad( group );
1572
1945
 
1573
- } );
1946
+ } )
1947
+ .catch( onError );
1574
1948
 
1575
1949
  }, onProgress, onError );
1576
1950
 
1577
1951
  }
1578
1952
 
1579
- parse( text, path, onLoad ) {
1953
+ parse( text, onLoad ) {
1580
1954
 
1581
- // Async parse. This function calls onParse with the parsed THREE.Object3D as parameter
1582
- const parsedInfo = this.parseCache.parse( text );
1583
- this.processObject( parsedInfo, null, path, this.rootParseScope )
1584
- .then( function ( result ) {
1955
+ this.partsCache
1956
+ .parseModel( text, this.materialLibrary )
1957
+ .then( group => {
1585
1958
 
1586
- onLoad( result.groupObject );
1959
+ this.computeConstructionSteps( group );
1960
+ onLoad( group );
1587
1961
 
1588
1962
  } );
1589
1963
 
@@ -1591,11 +1965,17 @@ class LDrawLoader extends Loader {
1591
1965
 
1592
1966
  setMaterials( materials ) {
1593
1967
 
1594
- // Clears parse scopes stack, adds new scope with material library
1595
- this.rootParseScope = this.newParseScopeLevel( materials );
1596
- this.rootParseScope.isFromParse = false;
1968
+ this.materialLibrary = {};
1969
+ this.materials = [];
1970
+ for ( let i = 0, l = materials.length; i < l; i ++ ) {
1971
+
1972
+ this.addMaterial( materials[ i ] );
1597
1973
 
1598
- this.materials = materials;
1974
+ }
1975
+
1976
+ // Add default main triangle and line edge materials (used in pieces that can be colored with a main color)
1977
+ this.addMaterial( this.parseColorMetaDirective( new LineParser( 'Main_Colour CODE 16 VALUE #FF8080 EDGE #333333' ) ) );
1978
+ this.addMaterial( this.parseColorMetaDirective( new LineParser( 'Edge_Colour CODE 24 VALUE #A0A0A0 EDGE #333333' ) ) );
1599
1979
 
1600
1980
  return this;
1601
1981
 
@@ -1609,121 +1989,145 @@ class LDrawLoader extends Loader {
1609
1989
 
1610
1990
  }
1611
1991
 
1612
- newParseScopeLevel( materials = null, parentScope = null ) {
1992
+ addMaterial( material ) {
1613
1993
 
1614
- // Adds a new scope level, assign materials to it and returns it
1994
+ // Adds a material to the material library which is on top of the parse scopes stack. And also to the materials array
1615
1995
 
1616
- const matLib = {};
1996
+ const matLib = this.materialLibrary;
1997
+ if ( ! matLib[ material.userData.code ] ) {
1617
1998
 
1618
- if ( materials ) {
1999
+ this.materials.push( material );
2000
+ matLib[ material.userData.code ] = material;
1619
2001
 
1620
- for ( let i = 0, n = materials.length; i < n; i ++ ) {
2002
+ }
1621
2003
 
1622
- const material = materials[ i ];
1623
- matLib[ material.userData.code ] = material;
2004
+ return this;
1624
2005
 
1625
- }
2006
+ }
1626
2007
 
1627
- }
2008
+ getMaterial( colorCode ) {
1628
2009
 
1629
- const newParseScope = {
1630
-
1631
- parentScope: parentScope,
1632
- lib: matLib,
1633
- url: null,
1634
-
1635
- // Subobjects
1636
- subobjects: null,
1637
- inverted: false,
1638
- category: null,
1639
- keywords: null,
1640
-
1641
- // Current subobject
1642
- currentFileName: null,
1643
- mainColorCode: parentScope ? parentScope.mainColorCode : MAIN_COLOUR_CODE,
1644
- mainEdgeColorCode: parentScope ? parentScope.mainEdgeColorCode : MAIN_EDGE_COLOUR_CODE,
1645
- matrix: new Matrix4(),
1646
- type: 'Model',
1647
- groupObject: null,
1648
-
1649
- // If false, it is a root material scope previous to parse
1650
- isFromParse: true,
1651
-
1652
- faces: [],
1653
- lineSegments: [],
1654
- conditionalSegments: [],
1655
- totalFaces: 0,
1656
- faceMaterials: new Set(),
1657
-
1658
- // If true, this object is the start of a construction step
1659
- startingConstructionStep: false
1660
- };
2010
+ if ( colorCode.startsWith( '0x2' ) ) {
2011
+
2012
+ // Special 'direct' material value (RGB color)
2013
+ const color = colorCode.substring( 3 );
1661
2014
 
1662
- return newParseScope;
2015
+ return this.parseColorMetaDirective( new LineParser( 'Direct_Color_' + color + ' CODE -1 VALUE #' + color + ' EDGE #' + color + '' ) );
2016
+
2017
+ }
2018
+
2019
+ return this.materialLibrary[ colorCode ] || null;
1663
2020
 
1664
2021
  }
1665
2022
 
1666
- addMaterial( material, parseScope ) {
2023
+ // Applies the appropriate materials to a prebuilt hierarchy of geometry. Assumes that color codes are present
2024
+ // in the material array if they need to be filled in.
2025
+ applyMaterialsToMesh( group, parentColorCode, materialHierarchy, finalMaterialPass = false ) {
1667
2026
 
1668
- // Adds a material to the material library which is on top of the parse scopes stack. And also to the materials array
2027
+ // find any missing materials as indicated by a color code string and replace it with a material from the current material lib
2028
+ const loader = this;
2029
+ const parentIsPassthrough = parentColorCode === MAIN_COLOUR_CODE;
2030
+ group.traverse( c => {
1669
2031
 
1670
- const matLib = parseScope.lib;
2032
+ if ( c.isMesh || c.isLineSegments ) {
1671
2033
 
1672
- if ( ! matLib[ material.userData.code ] ) {
2034
+ if ( Array.isArray( c.material ) ) {
1673
2035
 
1674
- this.materials.push( material );
2036
+ for ( let i = 0, l = c.material.length; i < l; i ++ ) {
1675
2037
 
1676
- }
2038
+ if ( ! c.material[ i ].isMaterial ) {
1677
2039
 
1678
- matLib[ material.userData.code ] = material;
2040
+ c.material[ i ] = getMaterial( c, c.material[ i ] );
1679
2041
 
1680
- return this;
2042
+ }
1681
2043
 
1682
- }
2044
+ }
1683
2045
 
1684
- getMaterial( colorCode, parseScope = this.rootParseScope ) {
2046
+ } else if ( ! c.material.isMaterial ) {
1685
2047
 
1686
- // Given a color code search its material in the parse scopes stack
2048
+ c.material = getMaterial( c, c.material );
1687
2049
 
1688
- if ( colorCode.startsWith( '0x2' ) ) {
2050
+ }
1689
2051
 
1690
- // Special 'direct' material value (RGB color)
2052
+ }
1691
2053
 
1692
- const color = colorCode.substring( 3 );
2054
+ } );
1693
2055
 
1694
- return this.parseColorMetaDirective( new LineParser( 'Direct_Color_' + color + ' CODE -1 VALUE #' + color + ' EDGE #' + color + '' ) );
1695
2056
 
1696
- }
2057
+ // Returns the appropriate material for the object (line or face) given color code. If the code is "pass through"
2058
+ // (24 for lines, 16 for edges) then the pass through color code is used. If that is also pass through then it's
2059
+ // simply returned for the subsequent material application.
2060
+ function getMaterial( c, colorCode ) {
2061
+
2062
+ // if our parent is a passthrough color code and we don't have the current material color available then
2063
+ // return early.
2064
+ if ( parentIsPassthrough && ! ( colorCode in materialHierarchy ) && ! finalMaterialPass ) {
2065
+
2066
+ return colorCode;
2067
+
2068
+ }
2069
+
2070
+ const forEdge = c.isLineSegments || c.isConditionalLine;
2071
+ const isPassthrough = ! forEdge && colorCode === MAIN_COLOUR_CODE || forEdge && colorCode === MAIN_EDGE_COLOUR_CODE;
2072
+ if ( isPassthrough ) {
2073
+
2074
+ colorCode = parentColorCode;
2075
+
2076
+ }
1697
2077
 
1698
- while ( parseScope ) {
2078
+ let material = null;
2079
+ if ( colorCode in materialHierarchy ) {
1699
2080
 
1700
- const material = parseScope.lib[ colorCode ];
2081
+ material = materialHierarchy[ colorCode ];
1701
2082
 
1702
- if ( material ) {
2083
+ } else if ( finalMaterialPass ) {
2084
+
2085
+ // see if we can get the final material from from the "getMaterial" function which will attempt to
2086
+ // parse the "direct" colors
2087
+ material = loader.getMaterial( colorCode );
2088
+ if ( material === null ) {
2089
+
2090
+ // otherwise throw an error if this is final opportunity to set the material
2091
+ throw new Error( `LDrawLoader: Material properties for code ${ colorCode } not available.` );
2092
+
2093
+ }
1703
2094
 
1704
- return material;
1705
2095
 
1706
2096
  } else {
1707
2097
 
1708
- parseScope = parseScope.parentScope;
2098
+ return colorCode;
1709
2099
 
1710
2100
  }
1711
2101
 
1712
- }
2102
+ if ( c.isLineSegments ) {
2103
+
2104
+ material = material.userData.edgeMaterial;
2105
+
2106
+ if ( c.isConditionalLine ) {
1713
2107
 
1714
- // Material was not found
1715
- return null;
2108
+ material = material.userData.conditionalEdgeMaterial;
2109
+
2110
+ }
2111
+
2112
+ }
2113
+
2114
+ return material;
2115
+
2116
+ }
1716
2117
 
1717
2118
  }
1718
2119
 
1719
2120
  getMainMaterial() {
1720
2121
 
1721
2122
  return this.getMaterial( MAIN_COLOUR_CODE );
2123
+
1722
2124
  }
1723
2125
 
1724
2126
  getMainEdgeMaterial() {
1725
2127
 
1726
- return this.getMaterial( MAIN_EDGE_COLOUR_CODE );;
2128
+ const mainMat = this.getMainMaterial();
2129
+ return mainMat && mainMat.userData ? mainMat.userData.edgeMaterial : null;
2130
+
1727
2131
  }
1728
2132
 
1729
2133
  parseColorMetaDirective( lineParser ) {
@@ -1926,6 +2330,7 @@ class LDrawLoader extends Loader {
1926
2330
  material.premultipliedAlpha = true;
1927
2331
  material.opacity = alpha;
1928
2332
  material.depthWrite = ! isTransparent;
2333
+ material.color.convertSRGBToLinear();
1929
2334
 
1930
2335
  material.polygonOffset = true;
1931
2336
  material.polygonOffsetFactor = 1;
@@ -1947,6 +2352,7 @@ class LDrawLoader extends Loader {
1947
2352
  } );
1948
2353
  edgeMaterial.userData.code = code;
1949
2354
  edgeMaterial.name = name + ' - Edge';
2355
+ edgeMaterial.color.convertSRGBToLinear();
1950
2356
 
1951
2357
  // This is the material used for conditional edges
1952
2358
  edgeMaterial.userData.conditionalEdgeMaterial = new LDrawConditionalLineMaterial( {
@@ -1958,6 +2364,7 @@ class LDrawLoader extends Loader {
1958
2364
  opacity: alpha,
1959
2365
 
1960
2366
  } );
2367
+ edgeMaterial.userData.conditionalEdgeMaterial.color.convertSRGBToLinear();
1961
2368
 
1962
2369
  }
1963
2370
 
@@ -1966,115 +2373,9 @@ class LDrawLoader extends Loader {
1966
2373
 
1967
2374
  material.userData.edgeMaterial = edgeMaterial;
1968
2375
 
1969
- return material;
1970
-
1971
- }
1972
-
1973
- //
1974
-
1975
- objectParse( info, parseScope ) {
1976
-
1977
- // Retrieve data from the parent parse scope
1978
- const currentParseScope = parseScope;
1979
- const parentParseScope = currentParseScope.parentScope;
1980
-
1981
- // Main color codes passed to this subobject (or default codes 16 and 24 if it is the root object)
1982
- const mainColorCode = currentParseScope.mainColorCode;
1983
- const mainEdgeColorCode = currentParseScope.mainEdgeColorCode;
1984
-
1985
- const parseColorCode = ( colorCode, forEdge ) => {
1986
-
1987
- // Parses next color code and returns a THREE.Material
1988
-
1989
- if ( ! forEdge && colorCode === MAIN_COLOUR_CODE ) {
1990
-
1991
- colorCode = mainColorCode;
1992
-
1993
- }
1994
-
1995
- if ( forEdge && colorCode === MAIN_EDGE_COLOUR_CODE ) {
1996
-
1997
- colorCode = mainEdgeColorCode;
1998
-
1999
- }
2000
-
2001
- const material = this.getMaterial( colorCode, currentParseScope );
2002
-
2003
- if ( ! material ) {
2004
-
2005
- throw new Error( 'LDrawLoader: Unknown color code "' + colorCode + '" is used but it was not defined previously.' );
2006
-
2007
- }
2008
-
2009
- return material;
2010
-
2011
- };
2012
-
2013
- const faces = info.faces;
2014
- const lineSegments = info.lineSegments;
2015
- const conditionalSegments = info.conditionalSegments;
2016
- const materials = info.materials;
2017
- if ( currentParseScope.inverted ) {
2018
-
2019
- faces.reverse();
2020
-
2021
- }
2022
-
2023
- for ( const colorCode in materials ) {
2024
-
2025
- this.addMaterial( materials[ colorCode ], currentParseScope );
2026
-
2027
- }
2028
-
2029
- for ( let i = 0, l = faces.length; i < l; i ++ ) {
2030
-
2031
- const face = faces[ i ];
2032
- if ( face.material === null ) {
2033
-
2034
- face.material = parseColorCode( face.colorCode, false );
2035
-
2036
- }
2037
-
2038
- }
2039
-
2040
- for ( let i = 0, l = lineSegments.length; i < l; i ++ ) {
2041
-
2042
- const ls = lineSegments[ i ];
2043
- if ( ls.material === null ) {
2044
-
2045
- ls.material = parseColorCode( ls.colorCode, true );
2046
-
2047
- }
2048
-
2049
- }
2050
-
2051
- for ( let i = 0, l = conditionalSegments.length; i < l; i ++ ) {
2052
-
2053
- const cs = conditionalSegments[ i ];
2054
- if ( cs.material === null ) {
2376
+ this.addMaterial( material );
2055
2377
 
2056
- cs.material = parseColorCode( cs.colorCode, true );
2057
-
2058
- }
2059
-
2060
- }
2061
-
2062
- currentParseScope.faces = info.faces;
2063
- currentParseScope.conditionalSegments = info.conditionalSegments;
2064
- currentParseScope.lineSegments = info.lineSegments;
2065
- currentParseScope.category = info.category;
2066
- currentParseScope.keywords = info.keywords;
2067
- currentParseScope.subobjects = info.subobjects;
2068
- currentParseScope.type = info.type;
2069
- currentParseScope.totalFaces = info.totalFaces;
2070
-
2071
- const isRoot = ! parentParseScope.isFromParse;
2072
- if ( isRoot || ! isPrimitiveType( info.type ) ) {
2073
-
2074
- currentParseScope.groupObject = new Group();
2075
- currentParseScope.groupObject.userData.startingConstructionStep = currentParseScope.startingConstructionStep;
2076
-
2077
- }
2378
+ return material;
2078
2379
 
2079
2380
  }
2080
2381
 
@@ -2104,215 +2405,6 @@ class LDrawLoader extends Loader {
2104
2405
 
2105
2406
  }
2106
2407
 
2107
- finalizeObject( subobjectParseScope ) {
2108
-
2109
- // fail gracefully if an object could not be loaded
2110
- if ( subobjectParseScope === null ) {
2111
-
2112
- return;
2113
-
2114
- }
2115
-
2116
- const parentParseScope = subobjectParseScope.parentScope;
2117
-
2118
- // Smooth the normals if this is a part or if this is a case where the subpart
2119
- // is added directly into the parent model (meaning it will never get smoothed by
2120
- // being added to a part)
2121
- const doSmooth =
2122
- isPartType( subobjectParseScope.type ) ||
2123
- (
2124
- isPrimitiveType( subobjectParseScope.type ) &&
2125
- isModelType( subobjectParseScope.parentScope.type )
2126
- );
2127
-
2128
- if ( this.smoothNormals && doSmooth ) {
2129
-
2130
- generateFaceNormals( subobjectParseScope.faces );
2131
-
2132
- // only check subsetgments if we have multiple materials in a single part because this seems to be the case where it's needed most --
2133
- // there may be cases where a single edge line crosses over polygon edges that are broken up by multiple materials.
2134
- const checkSubSegments = subobjectParseScope.faceMaterials.size > 1;
2135
- smoothNormals( subobjectParseScope.faces, subobjectParseScope.lineSegments, checkSubSegments );
2136
-
2137
- }
2138
-
2139
- const isRoot = ! parentParseScope.isFromParse;
2140
- if ( ! isPrimitiveType( subobjectParseScope.type ) || isRoot ) {
2141
-
2142
- const objGroup = subobjectParseScope.groupObject;
2143
-
2144
- if ( subobjectParseScope.faces.length > 0 ) {
2145
-
2146
- objGroup.add( createObject( subobjectParseScope.faces, 3, false, subobjectParseScope.totalFaces ) );
2147
-
2148
- }
2149
-
2150
- if ( subobjectParseScope.lineSegments.length > 0 ) {
2151
-
2152
- objGroup.add( createObject( subobjectParseScope.lineSegments, 2 ) );
2153
-
2154
- }
2155
-
2156
- if ( subobjectParseScope.conditionalSegments.length > 0 ) {
2157
-
2158
- objGroup.add( createObject( subobjectParseScope.conditionalSegments, 2, true ) );
2159
-
2160
- }
2161
-
2162
- if ( parentParseScope.groupObject ) {
2163
-
2164
- objGroup.name = subobjectParseScope.fileName;
2165
- objGroup.userData.category = subobjectParseScope.category;
2166
- objGroup.userData.keywords = subobjectParseScope.keywords;
2167
- subobjectParseScope.matrix.decompose( objGroup.position, objGroup.quaternion, objGroup.scale );
2168
-
2169
- parentParseScope.groupObject.add( objGroup );
2170
-
2171
- }
2172
-
2173
- } else {
2174
-
2175
- const parentLineSegments = parentParseScope.lineSegments;
2176
- const parentConditionalSegments = parentParseScope.conditionalSegments;
2177
- const parentFaces = parentParseScope.faces;
2178
- const parentFaceMaterials = parentParseScope.faceMaterials;
2179
-
2180
- const lineSegments = subobjectParseScope.lineSegments;
2181
- const conditionalSegments = subobjectParseScope.conditionalSegments;
2182
- const faces = subobjectParseScope.faces;
2183
- const faceMaterials = subobjectParseScope.faceMaterials;
2184
- const matrix = subobjectParseScope.matrix;
2185
- const matrixScaleInverted = matrix.determinant() < 0;
2186
-
2187
- for ( let i = 0, l = lineSegments.length; i < l; i ++ ) {
2188
-
2189
- const ls = lineSegments[ i ];
2190
- const vertices = ls.vertices;
2191
- vertices[ 0 ].applyMatrix4( matrix );
2192
- vertices[ 1 ].applyMatrix4( matrix );
2193
-
2194
- parentLineSegments.push( ls );
2195
-
2196
- }
2197
-
2198
- for ( let i = 0, l = conditionalSegments.length; i < l; i ++ ) {
2199
-
2200
- const os = conditionalSegments[ i ];
2201
- const vertices = os.vertices;
2202
- const controlPoints = os.controlPoints;
2203
- vertices[ 0 ].applyMatrix4( matrix );
2204
- vertices[ 1 ].applyMatrix4( matrix );
2205
- controlPoints[ 0 ].applyMatrix4( matrix );
2206
- controlPoints[ 1 ].applyMatrix4( matrix );
2207
-
2208
- parentConditionalSegments.push( os );
2209
-
2210
- }
2211
-
2212
- for ( let i = 0, l = faces.length; i < l; i ++ ) {
2213
-
2214
- const tri = faces[ i ];
2215
- const vertices = tri.vertices;
2216
- for ( let i = 0, l = vertices.length; i < l; i ++ ) {
2217
-
2218
- vertices[ i ].applyMatrix4( matrix );
2219
-
2220
- }
2221
-
2222
- // If the scale of the object is negated then the triangle winding order
2223
- // needs to be flipped.
2224
- if ( matrixScaleInverted !== subobjectParseScope.inverted ) {
2225
-
2226
- vertices.reverse();
2227
-
2228
- }
2229
-
2230
- parentFaces.push( tri );
2231
-
2232
- }
2233
-
2234
- parentParseScope.totalFaces += subobjectParseScope.totalFaces;
2235
- faceMaterials.forEach( material => parentFaceMaterials.add( material ) );
2236
-
2237
- }
2238
-
2239
- }
2240
-
2241
- async processObject( parsedInfo, subobject, url, parentScope ) {
2242
-
2243
- const scope = this;
2244
-
2245
- const parseScope = this.newParseScopeLevel( null, parentScope );
2246
- parseScope.url = url;
2247
-
2248
- // Set current matrix
2249
- if ( subobject ) {
2250
-
2251
- parseScope.matrix.copy( subobject.matrix );
2252
- parseScope.inverted = subobject.inverted;
2253
- parseScope.startingConstructionStep = subobject.startingConstructionStep;
2254
- parseScope.fileName = subobject.fileName;
2255
- if ( subobject.colorCode === MAIN_COLOUR_CODE && parentScope ) {
2256
-
2257
- parseScope.mainColorCode = parentScope.mainColorCode;
2258
- parseScope.mainEdgeColorCode = parentScope.mainEdgeColorCode;
2259
-
2260
- } else if ( subobject.colorCode !== MAIN_COLOUR_CODE ) {
2261
-
2262
- parseScope.mainColorCode = subobject.colorCode;
2263
- parseScope.mainEdgeColorCode = subobject.colorCode;
2264
-
2265
- }
2266
-
2267
- }
2268
-
2269
- // Parse the object
2270
- this.objectParse( parsedInfo, parseScope );
2271
-
2272
- const subobjects = parseScope.subobjects;
2273
- const promises = [];
2274
- for ( let i = 0, l = subobjects.length; i < l; i ++ ) {
2275
-
2276
- promises.push( loadSubobject( subobjects[ i ] ) );
2277
-
2278
- }
2279
-
2280
- // Kick off of the downloads in parallel but process all the subobjects
2281
- // in order so all the assembly instructions are correct
2282
- const subobjectScopes = await Promise.all( promises );
2283
- for ( let i = 0, l = subobjectScopes.length; i < l; i ++ ) {
2284
-
2285
- this.finalizeObject( subobjectScopes[ i ] );
2286
-
2287
- }
2288
-
2289
- // If it is root object then finalize this object and compute construction steps
2290
- if ( ! parentScope.isFromParse ) {
2291
-
2292
- this.finalizeObject( parseScope );
2293
- this.computeConstructionSteps( parseScope.groupObject );
2294
-
2295
- }
2296
-
2297
- return parseScope;
2298
-
2299
- function loadSubobject( subobject ) {
2300
-
2301
- return scope.parseCache.loadData( subobject.fileName ).then( function ( parsedInfo ) {
2302
-
2303
- return scope.processObject( parsedInfo, subobject, url, parseScope );
2304
-
2305
- } ).catch( function ( err ) {
2306
-
2307
- console.warn( err );
2308
- return null;
2309
-
2310
- } );
2311
-
2312
- }
2313
-
2314
- }
2315
-
2316
2408
  }
2317
2409
 
2318
2410
  export { LDrawLoader };