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.
- package/README.md +1 -1
- package/build/three.cjs +37132 -0
- package/build/three.js +71 -53
- package/build/three.min.js +2 -2
- package/build/three.module.js +80 -47
- package/examples/js/animation/MMDAnimationHelper.js +3 -1
- package/examples/js/controls/experimental/CameraControls.js +6 -2
- package/examples/js/exporters/GLTFExporter.js +82 -39
- package/examples/js/geometries/TextGeometry.js +12 -10
- package/examples/js/interactive/HTMLMesh.js +14 -2
- package/examples/js/loaders/EXRLoader.js +20 -9
- package/examples/js/loaders/GLTFLoader.js +1 -1
- package/examples/js/loaders/LDrawLoader.js +541 -425
- package/examples/js/loaders/MD2Loader.js +5 -5
- package/examples/js/loaders/MTLLoader.js +10 -3
- package/examples/js/loaders/PCDLoader.js +2 -2
- package/examples/js/loaders/STLLoader.js +1 -1
- package/examples/js/loaders/SVGLoader.js +3 -2
- package/examples/js/modifiers/EdgeSplitModifier.js +0 -6
- package/examples/js/modifiers/SimplifyModifier.js +0 -10
- package/examples/js/objects/Lensflare.js +2 -2
- package/examples/js/postprocessing/OutlinePass.js +1 -5
- package/examples/js/renderers/CSS2DRenderer.js +25 -19
- package/examples/js/renderers/CSS3DRenderer.js +37 -32
- package/examples/js/utils/LDrawUtils.js +182 -0
- package/examples/jsm/exporters/GLTFExporter.js +80 -12
- package/examples/jsm/geometries/TextGeometry.js +13 -13
- package/examples/jsm/interactive/HTMLMesh.js +16 -2
- package/examples/jsm/libs/flow.module.js +930 -169
- package/examples/jsm/loaders/EXRLoader.js +19 -9
- package/examples/jsm/loaders/GLTFLoader.js +1 -1
- package/examples/jsm/loaders/LDrawLoader.js +534 -442
- package/examples/jsm/loaders/MD2Loader.js +5 -5
- package/examples/jsm/loaders/MTLLoader.js +11 -4
- package/examples/jsm/loaders/PCDLoader.js +2 -2
- package/examples/jsm/loaders/STLLoader.js +1 -1
- package/examples/jsm/loaders/SVGLoader.js +3 -1
- package/examples/jsm/loaders/VRMLLoader.js +0 -1
- package/examples/jsm/modifiers/EdgeSplitModifier.js +0 -6
- package/examples/jsm/modifiers/SimplifyModifier.js +0 -10
- package/examples/jsm/node-editor/NodeEditor.js +435 -181
- package/examples/jsm/node-editor/accessors/NormalEditor.js +8 -8
- package/examples/jsm/node-editor/accessors/PositionEditor.js +8 -8
- package/examples/jsm/node-editor/accessors/UVEditor.js +6 -7
- package/examples/jsm/node-editor/core/BaseNode.js +83 -0
- package/examples/jsm/node-editor/display/BlendEditor.js +7 -6
- package/examples/jsm/node-editor/examples/animate-uv.json +1 -1
- package/examples/jsm/node-editor/examples/fake-top-light.json +1 -1
- package/examples/jsm/node-editor/examples/oscillator-color.json +1 -1
- package/examples/jsm/node-editor/examples/rim.json +1 -1
- package/examples/jsm/node-editor/inputs/ColorEditor.js +14 -10
- package/examples/jsm/node-editor/inputs/FloatEditor.js +6 -7
- package/examples/jsm/node-editor/inputs/SliderEditor.js +4 -5
- package/examples/jsm/node-editor/inputs/Vector2Editor.js +6 -7
- package/examples/jsm/node-editor/inputs/Vector3Editor.js +8 -9
- package/examples/jsm/node-editor/inputs/Vector4Editor.js +9 -10
- package/examples/jsm/node-editor/materials/BasicMaterialEditor.js +87 -0
- package/examples/jsm/node-editor/materials/PointsMaterialEditor.js +97 -0
- package/examples/jsm/node-editor/materials/StandardMaterialEditor.js +20 -17
- package/examples/jsm/node-editor/math/AngleEditor.js +39 -0
- package/examples/jsm/node-editor/math/DotEditor.js +6 -7
- package/examples/jsm/node-editor/math/InvertEditor.js +4 -3
- package/examples/jsm/node-editor/math/LimiterEditor.js +11 -10
- package/examples/jsm/node-editor/math/NormalizeEditor.js +6 -5
- package/examples/jsm/node-editor/math/OperatorEditor.js +12 -11
- package/examples/jsm/node-editor/math/PowerEditor.js +6 -5
- package/examples/jsm/node-editor/math/TrigonometryEditor.js +12 -7
- package/examples/jsm/node-editor/procedural/CheckerEditor.js +4 -3
- package/examples/jsm/node-editor/scene/MeshEditor.js +99 -0
- package/examples/jsm/node-editor/scene/Object3DEditor.js +160 -0
- package/examples/jsm/node-editor/scene/PointsEditor.js +99 -0
- package/examples/jsm/node-editor/utils/JoinEditor.js +58 -0
- package/examples/jsm/node-editor/utils/OscillatorEditor.js +7 -6
- package/examples/jsm/node-editor/utils/SplitEditor.js +39 -0
- package/examples/jsm/node-editor/utils/TimerEditor.js +6 -5
- package/examples/jsm/postprocessing/OutlinePass.js +1 -4
- package/examples/jsm/renderers/CSS2DRenderer.js +24 -19
- package/examples/jsm/renderers/CSS3DRenderer.js +36 -30
- package/examples/jsm/renderers/nodes/accessors/UVNode.js +4 -4
- package/examples/jsm/renderers/nodes/core/NodeBuilder.js +2 -1
- package/examples/jsm/renderers/nodes/utils/SplitNode.js +19 -4
- package/examples/jsm/utils/LDrawUtils.js +18 -11
- package/package.json +16 -9
- package/src/constants.js +1 -3
- package/src/core/BufferGeometry.js +2 -2
- package/src/geometries/LatheGeometry.js +1 -1
- package/src/loaders/FileLoader.js +20 -3
- package/src/math/Box3.js +24 -10
- package/src/renderers/WebGLRenderTarget.js +4 -1
- package/src/renderers/WebGLRenderer.js +3 -3
- package/src/renderers/shaders/ShaderChunk/morphnormal_vertex.glsl.js +1 -1
- package/src/renderers/shaders/ShaderChunk/morphtarget_vertex.glsl.js +2 -2
- package/src/renderers/webgl/WebGLBackground.js +2 -2
- package/src/renderers/webgl/WebGLGeometries.js +2 -2
- package/src/renderers/webgl/WebGLPrograms.js +2 -2
- package/src/renderers/webgl/WebGLState.js +2 -2
- package/src/renderers/webgl/WebGLTextures.js +9 -6
- package/src/renderers/webgl/WebGLUtils.js +1 -3
- package/src/renderers/webxr/WebXRManager.js +2 -0
- 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.
|
|
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
|
|
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
|
-
|
|
1234
|
+
// returns an (optionally cloned) instance of the data
|
|
1235
|
+
getData( fileName, clone = true ) {
|
|
1224
1236
|
|
|
1225
1237
|
const key = fileName.toLowerCase();
|
|
1226
|
-
|
|
1238
|
+
const result = this._cache[ key ];
|
|
1239
|
+
if ( result === null || result instanceof Promise ) {
|
|
1227
1240
|
|
|
1228
|
-
|
|
1241
|
+
return null;
|
|
1229
1242
|
|
|
1230
|
-
|
|
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
|
-
|
|
1255
|
+
}
|
|
1237
1256
|
|
|
1238
|
-
|
|
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.
|
|
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.
|
|
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 (
|
|
1732
|
+
if ( material !== null ) {
|
|
1380
1733
|
|
|
1381
|
-
|
|
1734
|
+
if ( elementSize === 3 ) {
|
|
1382
1735
|
|
|
1383
|
-
|
|
1736
|
+
materials.push( material );
|
|
1384
1737
|
|
|
1385
|
-
if (
|
|
1738
|
+
} else if ( elementSize === 2 ) {
|
|
1386
1739
|
|
|
1387
|
-
|
|
1740
|
+
if ( material !== null ) {
|
|
1388
1741
|
|
|
1389
|
-
|
|
1742
|
+
if ( isConditionalSegments ) {
|
|
1743
|
+
|
|
1744
|
+
materials.push( material.userData.edgeMaterial.userData.conditionalEdgeMaterial );
|
|
1390
1745
|
|
|
1391
|
-
|
|
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.
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
1568
|
-
|
|
1569
|
-
.then(
|
|
1938
|
+
this.partsCache
|
|
1939
|
+
.parseModel( text, this.materialLibrary )
|
|
1940
|
+
.then( group => {
|
|
1570
1941
|
|
|
1571
|
-
|
|
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,
|
|
1953
|
+
parse( text, onLoad ) {
|
|
1580
1954
|
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
.then( function ( result ) {
|
|
1955
|
+
this.partsCache
|
|
1956
|
+
.parseModel( text, this.materialLibrary )
|
|
1957
|
+
.then( group => {
|
|
1585
1958
|
|
|
1586
|
-
|
|
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
|
-
|
|
1595
|
-
this.
|
|
1596
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1992
|
+
addMaterial( material ) {
|
|
1613
1993
|
|
|
1614
|
-
// Adds a
|
|
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
|
-
|
|
1999
|
+
this.materials.push( material );
|
|
2000
|
+
matLib[ material.userData.code ] = material;
|
|
1619
2001
|
|
|
1620
|
-
|
|
2002
|
+
}
|
|
1621
2003
|
|
|
1622
|
-
|
|
1623
|
-
matLib[ material.userData.code ] = material;
|
|
2004
|
+
return this;
|
|
1624
2005
|
|
|
1625
|
-
|
|
2006
|
+
}
|
|
1626
2007
|
|
|
1627
|
-
|
|
2008
|
+
getMaterial( colorCode ) {
|
|
1628
2009
|
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
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
|
-
|
|
2032
|
+
if ( c.isMesh || c.isLineSegments ) {
|
|
1671
2033
|
|
|
1672
|
-
|
|
2034
|
+
if ( Array.isArray( c.material ) ) {
|
|
1673
2035
|
|
|
1674
|
-
|
|
2036
|
+
for ( let i = 0, l = c.material.length; i < l; i ++ ) {
|
|
1675
2037
|
|
|
1676
|
-
|
|
2038
|
+
if ( ! c.material[ i ].isMaterial ) {
|
|
1677
2039
|
|
|
1678
|
-
|
|
2040
|
+
c.material[ i ] = getMaterial( c, c.material[ i ] );
|
|
1679
2041
|
|
|
1680
|
-
|
|
2042
|
+
}
|
|
1681
2043
|
|
|
1682
|
-
|
|
2044
|
+
}
|
|
1683
2045
|
|
|
1684
|
-
|
|
2046
|
+
} else if ( ! c.material.isMaterial ) {
|
|
1685
2047
|
|
|
1686
|
-
|
|
2048
|
+
c.material = getMaterial( c, c.material );
|
|
1687
2049
|
|
|
1688
|
-
|
|
2050
|
+
}
|
|
1689
2051
|
|
|
1690
|
-
|
|
2052
|
+
}
|
|
1691
2053
|
|
|
1692
|
-
|
|
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
|
-
|
|
2078
|
+
let material = null;
|
|
2079
|
+
if ( colorCode in materialHierarchy ) {
|
|
1699
2080
|
|
|
1700
|
-
|
|
2081
|
+
material = materialHierarchy[ colorCode ];
|
|
1701
2082
|
|
|
1702
|
-
if (
|
|
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
|
-
|
|
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
|
-
|
|
1715
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 };
|