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
|
@@ -17,6 +17,8 @@
|
|
|
17
17
|
const FILE_LOCATION_TRY_RELATIVE = 4;
|
|
18
18
|
const FILE_LOCATION_TRY_ABSOLUTE = 5;
|
|
19
19
|
const FILE_LOCATION_NOT_FOUND = 6;
|
|
20
|
+
const MAIN_COLOUR_CODE = '16';
|
|
21
|
+
const MAIN_EDGE_COLOUR_CODE = '24';
|
|
20
22
|
|
|
21
23
|
const _tempVec0 = new THREE.Vector3();
|
|
22
24
|
|
|
@@ -142,6 +144,17 @@
|
|
|
142
144
|
|
|
143
145
|
}
|
|
144
146
|
|
|
147
|
+
class ConditionalLineSegments extends THREE.LineSegments {
|
|
148
|
+
|
|
149
|
+
constructor( geometry, material ) {
|
|
150
|
+
|
|
151
|
+
super( geometry, material );
|
|
152
|
+
this.isConditionalLine = true;
|
|
153
|
+
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
}
|
|
157
|
+
|
|
145
158
|
function generateFaceNormals( faces ) {
|
|
146
159
|
|
|
147
160
|
for ( let i = 0, l = faces.length; i < l; i ++ ) {
|
|
@@ -497,13 +510,7 @@
|
|
|
497
510
|
|
|
498
511
|
function isPartType( type ) {
|
|
499
512
|
|
|
500
|
-
return type === 'Part';
|
|
501
|
-
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
function isModelType( type ) {
|
|
505
|
-
|
|
506
|
-
return type === 'Model' || type === 'Unofficial_Model';
|
|
513
|
+
return type === 'Part' || type === 'Unofficial_Part';
|
|
507
514
|
|
|
508
515
|
}
|
|
509
516
|
|
|
@@ -597,14 +604,15 @@
|
|
|
597
604
|
|
|
598
605
|
}
|
|
599
606
|
|
|
600
|
-
}
|
|
607
|
+
} // Fetches and parses an intermediate representation of LDraw parts files.
|
|
608
|
+
|
|
601
609
|
|
|
602
610
|
class LDrawParsedCache {
|
|
603
611
|
|
|
604
612
|
constructor( loader ) {
|
|
605
613
|
|
|
606
614
|
this.loader = loader;
|
|
607
|
-
this.
|
|
615
|
+
this._cache = {};
|
|
608
616
|
|
|
609
617
|
}
|
|
610
618
|
|
|
@@ -651,6 +659,7 @@
|
|
|
651
659
|
result.totalFaces = original.totalFaces;
|
|
652
660
|
result.startingConstructionStep = original.startingConstructionStep;
|
|
653
661
|
result.materials = original.materials;
|
|
662
|
+
result.group = null;
|
|
654
663
|
return result;
|
|
655
664
|
|
|
656
665
|
}
|
|
@@ -733,7 +742,7 @@
|
|
|
733
742
|
|
|
734
743
|
}
|
|
735
744
|
|
|
736
|
-
parse( text ) {
|
|
745
|
+
parse( text, fileName = null ) {
|
|
737
746
|
|
|
738
747
|
const loader = this.loader; // final results
|
|
739
748
|
|
|
@@ -745,7 +754,7 @@
|
|
|
745
754
|
|
|
746
755
|
const getLocalMaterial = colorCode => {
|
|
747
756
|
|
|
748
|
-
return
|
|
757
|
+
return materials[ colorCode ] || null;
|
|
749
758
|
|
|
750
759
|
};
|
|
751
760
|
|
|
@@ -1135,37 +1144,376 @@
|
|
|
1135
1144
|
subobjects,
|
|
1136
1145
|
totalFaces,
|
|
1137
1146
|
startingConstructionStep,
|
|
1138
|
-
materials
|
|
1147
|
+
materials,
|
|
1148
|
+
fileName,
|
|
1149
|
+
group: null
|
|
1139
1150
|
};
|
|
1140
1151
|
|
|
1141
|
-
}
|
|
1152
|
+
} // returns an (optionally cloned) instance of the data
|
|
1142
1153
|
|
|
1143
|
-
|
|
1154
|
+
|
|
1155
|
+
getData( fileName, clone = true ) {
|
|
1144
1156
|
|
|
1145
1157
|
const key = fileName.toLowerCase();
|
|
1158
|
+
const result = this._cache[ key ];
|
|
1159
|
+
|
|
1160
|
+
if ( result === null || result instanceof Promise ) {
|
|
1161
|
+
|
|
1162
|
+
return null;
|
|
1163
|
+
|
|
1164
|
+
}
|
|
1165
|
+
|
|
1166
|
+
if ( clone ) {
|
|
1167
|
+
|
|
1168
|
+
return this.cloneResult( result );
|
|
1169
|
+
|
|
1170
|
+
} else {
|
|
1146
1171
|
|
|
1147
|
-
|
|
1172
|
+
return result;
|
|
1148
1173
|
|
|
1149
|
-
|
|
1174
|
+
}
|
|
1150
1175
|
|
|
1151
|
-
|
|
1176
|
+
} // kicks off a fetch and parse of the requested data if it hasn't already been loaded. Returns when
|
|
1177
|
+
// the data is ready to use and can be retrieved synchronously with "getData".
|
|
1178
|
+
|
|
1179
|
+
|
|
1180
|
+
async ensureDataLoaded( fileName ) {
|
|
1181
|
+
|
|
1182
|
+
const key = fileName.toLowerCase();
|
|
1183
|
+
|
|
1184
|
+
if ( ! ( key in this._cache ) ) {
|
|
1185
|
+
|
|
1186
|
+
// replace the promise with a copy of the parsed data for immediate processing
|
|
1187
|
+
this._cache[ key ] = this.fetchData( fileName ).then( text => {
|
|
1188
|
+
|
|
1189
|
+
const info = this.parse( text, fileName );
|
|
1190
|
+
this._cache[ key ] = info;
|
|
1191
|
+
return info;
|
|
1152
1192
|
|
|
1153
1193
|
} );
|
|
1154
1194
|
|
|
1155
1195
|
}
|
|
1156
1196
|
|
|
1157
|
-
|
|
1197
|
+
await this._cache[ key ];
|
|
1158
1198
|
|
|
1159
|
-
|
|
1199
|
+
} // sets the data in the cache from parsed data
|
|
1160
1200
|
|
|
1161
|
-
|
|
1201
|
+
|
|
1202
|
+
setData( fileName, text ) {
|
|
1203
|
+
|
|
1204
|
+
const key = fileName.toLowerCase();
|
|
1205
|
+
this._cache[ key ] = this.parse( text, fileName );
|
|
1162
1206
|
|
|
1163
1207
|
}
|
|
1164
1208
|
|
|
1165
|
-
|
|
1209
|
+
} // returns the material for an associated color code. If the color code is 16 for a face or 24 for
|
|
1210
|
+
// an edge then the passthroughColorCode is used.
|
|
1211
|
+
|
|
1212
|
+
|
|
1213
|
+
function getMaterialFromCode( colorCode, parentColorCode, materialHierarchy, forEdge ) {
|
|
1214
|
+
|
|
1215
|
+
const isPassthrough = ! forEdge && colorCode === MAIN_COLOUR_CODE || forEdge && colorCode === MAIN_EDGE_COLOUR_CODE;
|
|
1216
|
+
|
|
1217
|
+
if ( isPassthrough ) {
|
|
1218
|
+
|
|
1219
|
+
colorCode = parentColorCode;
|
|
1220
|
+
|
|
1221
|
+
}
|
|
1222
|
+
|
|
1223
|
+
return materialHierarchy[ colorCode ] || null;
|
|
1224
|
+
|
|
1225
|
+
} // Class used to parse and build LDraw parts as three.js objects and cache them if they're a "Part" type.
|
|
1226
|
+
|
|
1227
|
+
|
|
1228
|
+
class LDrawPartsGeometryCache {
|
|
1229
|
+
|
|
1230
|
+
constructor( loader ) {
|
|
1231
|
+
|
|
1232
|
+
this.loader = loader;
|
|
1233
|
+
this.parseCache = new LDrawParsedCache( loader );
|
|
1234
|
+
this._cache = {};
|
|
1235
|
+
|
|
1236
|
+
} // Convert the given file information into a mesh by processing subobjects.
|
|
1237
|
+
|
|
1238
|
+
|
|
1239
|
+
async processIntoMesh( info ) {
|
|
1240
|
+
|
|
1241
|
+
const loader = this.loader;
|
|
1242
|
+
const parseCache = this.parseCache;
|
|
1243
|
+
const faceMaterials = new Set(); // Processes the part subobject information to load child parts and merge geometry onto part
|
|
1244
|
+
// piece object.
|
|
1245
|
+
|
|
1246
|
+
const processInfoSubobjects = async ( info, subobject = null ) => {
|
|
1247
|
+
|
|
1248
|
+
const subobjects = info.subobjects;
|
|
1249
|
+
const promises = []; // Trigger load of all subobjects. If a subobject isn't a primitive then load it as a separate
|
|
1250
|
+
// group which lets instruction steps apply correctly.
|
|
1251
|
+
|
|
1252
|
+
for ( let i = 0, l = subobjects.length; i < l; i ++ ) {
|
|
1253
|
+
|
|
1254
|
+
const subobject = subobjects[ i ];
|
|
1255
|
+
const promise = parseCache.ensureDataLoaded( subobject.fileName ).then( () => {
|
|
1256
|
+
|
|
1257
|
+
const subobjectInfo = parseCache.getData( subobject.fileName, false );
|
|
1258
|
+
|
|
1259
|
+
if ( ! isPrimitiveType( subobjectInfo.type ) ) {
|
|
1260
|
+
|
|
1261
|
+
return this.loadModel( subobject.fileName ).catch( error => {
|
|
1262
|
+
|
|
1263
|
+
console.warn( error );
|
|
1264
|
+
return null;
|
|
1265
|
+
|
|
1266
|
+
} );
|
|
1267
|
+
|
|
1268
|
+
}
|
|
1269
|
+
|
|
1270
|
+
return processInfoSubobjects( parseCache.getData( subobject.fileName ), subobject );
|
|
1271
|
+
|
|
1272
|
+
} );
|
|
1273
|
+
promises.push( promise );
|
|
1274
|
+
|
|
1275
|
+
}
|
|
1276
|
+
|
|
1277
|
+
const group = new THREE.Group();
|
|
1278
|
+
group.userData.category = info.category;
|
|
1279
|
+
group.userData.keywords = info.keywords;
|
|
1280
|
+
info.group = group;
|
|
1281
|
+
const subobjectInfos = await Promise.all( promises );
|
|
1282
|
+
|
|
1283
|
+
for ( let i = 0, l = subobjectInfos.length; i < l; i ++ ) {
|
|
1284
|
+
|
|
1285
|
+
const subobject = info.subobjects[ i ];
|
|
1286
|
+
const subobjectInfo = subobjectInfos[ i ];
|
|
1287
|
+
|
|
1288
|
+
if ( subobjectInfo === null ) {
|
|
1289
|
+
|
|
1290
|
+
// the subobject failed to load
|
|
1291
|
+
continue;
|
|
1292
|
+
|
|
1293
|
+
} // if the subobject was loaded as a separate group then apply the parent scopes materials
|
|
1294
|
+
|
|
1295
|
+
|
|
1296
|
+
if ( subobjectInfo.isGroup ) {
|
|
1297
|
+
|
|
1298
|
+
const subobjectGroup = subobjectInfo;
|
|
1299
|
+
subobject.matrix.decompose( subobjectGroup.position, subobjectGroup.quaternion, subobjectGroup.scale );
|
|
1300
|
+
subobjectGroup.userData.startingConstructionStep = subobject.startingConstructionStep;
|
|
1301
|
+
subobjectGroup.name = subobject.fileName;
|
|
1302
|
+
loader.applyMaterialsToMesh( subobjectGroup, subobject.colorCode, info.materials );
|
|
1303
|
+
group.add( subobjectGroup );
|
|
1304
|
+
continue;
|
|
1305
|
+
|
|
1306
|
+
} // add the subobject group if it has children in case it has both children and primitives
|
|
1307
|
+
|
|
1308
|
+
|
|
1309
|
+
if ( subobjectInfo.group.children.length ) {
|
|
1310
|
+
|
|
1311
|
+
group.add( subobjectInfo.group );
|
|
1312
|
+
|
|
1313
|
+
} // transform the primitives into the local space of the parent piece and append them to
|
|
1314
|
+
// to the parent primitives list.
|
|
1315
|
+
|
|
1316
|
+
|
|
1317
|
+
const parentLineSegments = info.lineSegments;
|
|
1318
|
+
const parentConditionalSegments = info.conditionalSegments;
|
|
1319
|
+
const parentFaces = info.faces;
|
|
1320
|
+
const lineSegments = subobjectInfo.lineSegments;
|
|
1321
|
+
const conditionalSegments = subobjectInfo.conditionalSegments;
|
|
1322
|
+
const faces = subobjectInfo.faces;
|
|
1323
|
+
const matrix = subobject.matrix;
|
|
1324
|
+
const inverted = subobject.inverted;
|
|
1325
|
+
const matrixScaleInverted = matrix.determinant() < 0;
|
|
1326
|
+
const colorCode = subobject.colorCode;
|
|
1327
|
+
const lineColorCode = colorCode === MAIN_COLOUR_CODE ? MAIN_EDGE_COLOUR_CODE : colorCode;
|
|
1328
|
+
|
|
1329
|
+
for ( let i = 0, l = lineSegments.length; i < l; i ++ ) {
|
|
1330
|
+
|
|
1331
|
+
const ls = lineSegments[ i ];
|
|
1332
|
+
const vertices = ls.vertices;
|
|
1333
|
+
vertices[ 0 ].applyMatrix4( matrix );
|
|
1334
|
+
vertices[ 1 ].applyMatrix4( matrix );
|
|
1335
|
+
ls.colorCode = ls.colorCode === MAIN_EDGE_COLOUR_CODE ? lineColorCode : ls.colorCode;
|
|
1336
|
+
ls.material = ls.material || getMaterialFromCode( ls.colorCode, ls.colorCode, info.materials, true );
|
|
1337
|
+
parentLineSegments.push( ls );
|
|
1338
|
+
|
|
1339
|
+
}
|
|
1340
|
+
|
|
1341
|
+
for ( let i = 0, l = conditionalSegments.length; i < l; i ++ ) {
|
|
1342
|
+
|
|
1343
|
+
const os = conditionalSegments[ i ];
|
|
1344
|
+
const vertices = os.vertices;
|
|
1345
|
+
const controlPoints = os.controlPoints;
|
|
1346
|
+
vertices[ 0 ].applyMatrix4( matrix );
|
|
1347
|
+
vertices[ 1 ].applyMatrix4( matrix );
|
|
1348
|
+
controlPoints[ 0 ].applyMatrix4( matrix );
|
|
1349
|
+
controlPoints[ 1 ].applyMatrix4( matrix );
|
|
1350
|
+
os.colorCode = os.colorCode === MAIN_EDGE_COLOUR_CODE ? lineColorCode : os.colorCode;
|
|
1351
|
+
os.material = os.material || getMaterialFromCode( os.colorCode, os.colorCode, info.materials, true );
|
|
1352
|
+
parentConditionalSegments.push( os );
|
|
1353
|
+
|
|
1354
|
+
}
|
|
1355
|
+
|
|
1356
|
+
for ( let i = 0, l = faces.length; i < l; i ++ ) {
|
|
1357
|
+
|
|
1358
|
+
const tri = faces[ i ];
|
|
1359
|
+
const vertices = tri.vertices;
|
|
1360
|
+
|
|
1361
|
+
for ( let i = 0, l = vertices.length; i < l; i ++ ) {
|
|
1362
|
+
|
|
1363
|
+
vertices[ i ].applyMatrix4( matrix );
|
|
1364
|
+
|
|
1365
|
+
}
|
|
1366
|
+
|
|
1367
|
+
tri.colorCode = tri.colorCode === MAIN_COLOUR_CODE ? colorCode : tri.colorCode;
|
|
1368
|
+
tri.material = tri.material || getMaterialFromCode( tri.colorCode, colorCode, info.materials, false );
|
|
1369
|
+
faceMaterials.add( tri.colorCode ); // If the scale of the object is negated then the triangle winding order
|
|
1370
|
+
// needs to be flipped.
|
|
1371
|
+
|
|
1372
|
+
if ( matrixScaleInverted !== inverted ) {
|
|
1373
|
+
|
|
1374
|
+
vertices.reverse();
|
|
1375
|
+
|
|
1376
|
+
}
|
|
1166
1377
|
|
|
1378
|
+
parentFaces.push( tri );
|
|
1379
|
+
|
|
1380
|
+
}
|
|
1381
|
+
|
|
1382
|
+
info.totalFaces += subobjectInfo.totalFaces;
|
|
1383
|
+
|
|
1384
|
+
} // Apply the parent subobjects pass through material code to this object. This is done several times due
|
|
1385
|
+
// to material scoping.
|
|
1386
|
+
|
|
1387
|
+
|
|
1388
|
+
if ( subobject ) {
|
|
1389
|
+
|
|
1390
|
+
loader.applyMaterialsToMesh( group, subobject.colorCode, info.materials );
|
|
1391
|
+
|
|
1392
|
+
}
|
|
1393
|
+
|
|
1394
|
+
return info;
|
|
1395
|
+
|
|
1396
|
+
}; // Track material use to see if we need to use the normal smooth slow path for hard edges.
|
|
1397
|
+
|
|
1398
|
+
|
|
1399
|
+
for ( let i = 0, l = info.faces; i < l; i ++ ) {
|
|
1400
|
+
|
|
1401
|
+
faceMaterials.add( info.faces[ i ].colorCode );
|
|
1402
|
+
|
|
1403
|
+
}
|
|
1404
|
+
|
|
1405
|
+
await processInfoSubobjects( info );
|
|
1406
|
+
|
|
1407
|
+
if ( loader.smoothNormals ) {
|
|
1408
|
+
|
|
1409
|
+
const checkSubSegments = faceMaterials.size > 1;
|
|
1410
|
+
generateFaceNormals( info.faces );
|
|
1411
|
+
smoothNormals( info.faces, info.lineSegments, checkSubSegments );
|
|
1412
|
+
|
|
1413
|
+
} // Add the primitive objects and metadata.
|
|
1414
|
+
|
|
1415
|
+
|
|
1416
|
+
const group = info.group;
|
|
1417
|
+
|
|
1418
|
+
if ( info.faces.length > 0 ) {
|
|
1419
|
+
|
|
1420
|
+
group.add( createObject( info.faces, 3, false, info.totalFaces ) );
|
|
1421
|
+
|
|
1422
|
+
}
|
|
1423
|
+
|
|
1424
|
+
if ( info.lineSegments.length > 0 ) {
|
|
1425
|
+
|
|
1426
|
+
group.add( createObject( info.lineSegments, 2 ) );
|
|
1427
|
+
|
|
1428
|
+
}
|
|
1429
|
+
|
|
1430
|
+
if ( info.conditionalSegments.length > 0 ) {
|
|
1431
|
+
|
|
1432
|
+
group.add( createObject( info.conditionalSegments, 2, true ) );
|
|
1433
|
+
|
|
1434
|
+
}
|
|
1435
|
+
|
|
1436
|
+
return group;
|
|
1437
|
+
|
|
1438
|
+
}
|
|
1439
|
+
|
|
1440
|
+
hasCachedModel( fileName ) {
|
|
1441
|
+
|
|
1442
|
+
return fileName !== null && fileName.toLowerCase() in this._cache;
|
|
1443
|
+
|
|
1444
|
+
}
|
|
1445
|
+
|
|
1446
|
+
async getCachedModel( fileName ) {
|
|
1447
|
+
|
|
1448
|
+
if ( fileName !== null && this.hasCachedModel( fileName ) ) {
|
|
1449
|
+
|
|
1450
|
+
const key = fileName.toLowerCase();
|
|
1451
|
+
const group = await this._cache[ key ];
|
|
1452
|
+
return group.clone();
|
|
1453
|
+
|
|
1454
|
+
} else {
|
|
1455
|
+
|
|
1456
|
+
return null;
|
|
1457
|
+
|
|
1458
|
+
}
|
|
1459
|
+
|
|
1460
|
+
} // Loads and parses the model with the given file name. Returns a cached copy if available.
|
|
1461
|
+
|
|
1462
|
+
|
|
1463
|
+
async loadModel( fileName ) {
|
|
1464
|
+
|
|
1465
|
+
const parseCache = this.parseCache;
|
|
1167
1466
|
const key = fileName.toLowerCase();
|
|
1168
|
-
|
|
1467
|
+
|
|
1468
|
+
if ( this.hasCachedModel( fileName ) ) {
|
|
1469
|
+
|
|
1470
|
+
// Return cached model if available.
|
|
1471
|
+
return this.getCachedModel( fileName );
|
|
1472
|
+
|
|
1473
|
+
} else {
|
|
1474
|
+
|
|
1475
|
+
// Otherwise parse a new model.
|
|
1476
|
+
// Ensure the file data is loaded and pre parsed.
|
|
1477
|
+
await parseCache.ensureDataLoaded( fileName );
|
|
1478
|
+
const info = parseCache.getData( fileName );
|
|
1479
|
+
const promise = this.processIntoMesh( info ); // Now that the file has loaded it's possible that another part parse has been waiting in parallel
|
|
1480
|
+
// so check the cache again to see if it's been added since the last async operation so we don't
|
|
1481
|
+
// do unnecessary work.
|
|
1482
|
+
|
|
1483
|
+
if ( this.hasCachedModel( fileName ) ) {
|
|
1484
|
+
|
|
1485
|
+
return this.getCachedModel( fileName );
|
|
1486
|
+
|
|
1487
|
+
} // Cache object if it's a part so it can be reused later.
|
|
1488
|
+
|
|
1489
|
+
|
|
1490
|
+
if ( isPartType( info.type ) ) {
|
|
1491
|
+
|
|
1492
|
+
this._cache[ key ] = promise;
|
|
1493
|
+
|
|
1494
|
+
} // return a copy
|
|
1495
|
+
|
|
1496
|
+
|
|
1497
|
+
const group = await promise;
|
|
1498
|
+
return group.clone();
|
|
1499
|
+
|
|
1500
|
+
}
|
|
1501
|
+
|
|
1502
|
+
} // parses the given model text into a renderable object. Returns cached copy if available.
|
|
1503
|
+
|
|
1504
|
+
|
|
1505
|
+
async parseModel( text ) {
|
|
1506
|
+
|
|
1507
|
+
const parseCache = this.parseCache;
|
|
1508
|
+
const info = parseCache.parse( text );
|
|
1509
|
+
|
|
1510
|
+
if ( isPartType( info.type ) && this.hasCachedModel( info.fileName ) ) {
|
|
1511
|
+
|
|
1512
|
+
return this.getCachedModel( info.fileName );
|
|
1513
|
+
|
|
1514
|
+
}
|
|
1515
|
+
|
|
1516
|
+
return this.processIntoMesh( info );
|
|
1169
1517
|
|
|
1170
1518
|
}
|
|
1171
1519
|
|
|
@@ -1290,7 +1638,7 @@
|
|
|
1290
1638
|
|
|
1291
1639
|
}
|
|
1292
1640
|
|
|
1293
|
-
if ( prevMaterial !== elem.
|
|
1641
|
+
if ( prevMaterial !== elem.colorCode ) {
|
|
1294
1642
|
|
|
1295
1643
|
if ( prevMaterial !== null ) {
|
|
1296
1644
|
|
|
@@ -1300,25 +1648,43 @@
|
|
|
1300
1648
|
|
|
1301
1649
|
const material = elem.material;
|
|
1302
1650
|
|
|
1303
|
-
if (
|
|
1651
|
+
if ( material !== null ) {
|
|
1304
1652
|
|
|
1305
|
-
|
|
1653
|
+
if ( elementSize === 3 ) {
|
|
1306
1654
|
|
|
1307
|
-
|
|
1655
|
+
materials.push( material );
|
|
1308
1656
|
|
|
1309
|
-
if (
|
|
1657
|
+
} else if ( elementSize === 2 ) {
|
|
1310
1658
|
|
|
1311
|
-
|
|
1659
|
+
if ( material !== null ) {
|
|
1312
1660
|
|
|
1313
|
-
|
|
1661
|
+
if ( isConditionalSegments ) {
|
|
1662
|
+
|
|
1663
|
+
materials.push( material.userData.edgeMaterial.userData.conditionalEdgeMaterial );
|
|
1664
|
+
|
|
1665
|
+
} else {
|
|
1314
1666
|
|
|
1315
|
-
|
|
1667
|
+
materials.push( material.userData.edgeMaterial );
|
|
1668
|
+
|
|
1669
|
+
}
|
|
1670
|
+
|
|
1671
|
+
} else {
|
|
1672
|
+
|
|
1673
|
+
materials.push( null );
|
|
1674
|
+
|
|
1675
|
+
}
|
|
1316
1676
|
|
|
1317
1677
|
}
|
|
1318
1678
|
|
|
1679
|
+
} else {
|
|
1680
|
+
|
|
1681
|
+
// If a material has not been made available yet then keep the color code string in the material array
|
|
1682
|
+
// to save the spot for the material once a parent scopes materials are being applied to the object.
|
|
1683
|
+
materials.push( elem.colorCode );
|
|
1684
|
+
|
|
1319
1685
|
}
|
|
1320
1686
|
|
|
1321
|
-
prevMaterial = elem.
|
|
1687
|
+
prevMaterial = elem.colorCode;
|
|
1322
1688
|
index0 = offset / 3;
|
|
1323
1689
|
numGroupVerts = vertices.length;
|
|
1324
1690
|
|
|
@@ -1350,7 +1716,15 @@
|
|
|
1350
1716
|
|
|
1351
1717
|
if ( elementSize === 2 ) {
|
|
1352
1718
|
|
|
1353
|
-
|
|
1719
|
+
if ( isConditionalSegments ) {
|
|
1720
|
+
|
|
1721
|
+
object3d = new ConditionalLineSegments( bufferGeometry, materials.length === 1 ? materials[ 0 ] : materials );
|
|
1722
|
+
|
|
1723
|
+
} else {
|
|
1724
|
+
|
|
1725
|
+
object3d = new THREE.LineSegments( bufferGeometry, materials.length === 1 ? materials[ 0 ] : materials );
|
|
1726
|
+
|
|
1727
|
+
}
|
|
1354
1728
|
|
|
1355
1729
|
} else if ( elementSize === 3 ) {
|
|
1356
1730
|
|
|
@@ -1413,15 +1787,14 @@
|
|
|
1413
1787
|
|
|
1414
1788
|
super( manager ); // Array of THREE.Material
|
|
1415
1789
|
|
|
1416
|
-
this.materials = [];
|
|
1417
|
-
// This also allows to handle the embedded text files ("0 FILE" lines)
|
|
1790
|
+
this.materials = [];
|
|
1791
|
+
this.materialLibrary = {}; // This also allows to handle the embedded text files ("0 FILE" lines)
|
|
1418
1792
|
|
|
1419
|
-
this.
|
|
1793
|
+
this.partsCache = new LDrawPartsGeometryCache( this ); // This object is a map from file names to paths. It agilizes the paths search. If it is not set then files will be searched by trial and error.
|
|
1420
1794
|
|
|
1421
|
-
this.fileMap = {};
|
|
1422
|
-
this.rootParseScope = this.newParseScopeLevel(); // Add default main triangle and line edge materials (used in pieces that can be colored with a main color)
|
|
1795
|
+
this.fileMap = {}; // Initializes the materials library with default materials
|
|
1423
1796
|
|
|
1424
|
-
this.setMaterials( [
|
|
1797
|
+
this.setMaterials( [] ); // If this flag is set to true the vertex normals will be smoothed.
|
|
1425
1798
|
|
|
1426
1799
|
this.smoothNormals = true; // The path to load parts from the LDraw parts library from.
|
|
1427
1800
|
|
|
@@ -1473,24 +1846,24 @@
|
|
|
1473
1846
|
fileLoader.setWithCredentials( this.withCredentials );
|
|
1474
1847
|
fileLoader.load( url, text => {
|
|
1475
1848
|
|
|
1476
|
-
|
|
1477
|
-
this.processObject( parsedInfo, null, url, this.rootParseScope ).then( function ( result ) {
|
|
1849
|
+
this.partsCache.parseModel( text, this.materialLibrary ).then( group => {
|
|
1478
1850
|
|
|
1479
|
-
|
|
1851
|
+
this.applyMaterialsToMesh( group, MAIN_COLOUR_CODE, this.materialLibrary, true );
|
|
1852
|
+
this.computeConstructionSteps( group );
|
|
1853
|
+
onLoad( group );
|
|
1480
1854
|
|
|
1481
|
-
} );
|
|
1855
|
+
} ).catch( onError );
|
|
1482
1856
|
|
|
1483
1857
|
}, onProgress, onError );
|
|
1484
1858
|
|
|
1485
1859
|
}
|
|
1486
1860
|
|
|
1487
|
-
parse( text,
|
|
1861
|
+
parse( text, onLoad ) {
|
|
1488
1862
|
|
|
1489
|
-
|
|
1490
|
-
const parsedInfo = this.parseCache.parse( text );
|
|
1491
|
-
this.processObject( parsedInfo, null, path, this.rootParseScope ).then( function ( result ) {
|
|
1863
|
+
this.partsCache.parseModel( text, this.materialLibrary ).then( group => {
|
|
1492
1864
|
|
|
1493
|
-
|
|
1865
|
+
this.computeConstructionSteps( group );
|
|
1866
|
+
onLoad( group );
|
|
1494
1867
|
|
|
1495
1868
|
} );
|
|
1496
1869
|
|
|
@@ -1498,88 +1871,47 @@
|
|
|
1498
1871
|
|
|
1499
1872
|
setMaterials( materials ) {
|
|
1500
1873
|
|
|
1501
|
-
|
|
1502
|
-
this.
|
|
1503
|
-
this.rootParseScope.isFromParse = false;
|
|
1504
|
-
this.materials = materials;
|
|
1505
|
-
return this;
|
|
1506
|
-
|
|
1507
|
-
}
|
|
1508
|
-
|
|
1509
|
-
setFileMap( fileMap ) {
|
|
1510
|
-
|
|
1511
|
-
this.fileMap = fileMap;
|
|
1512
|
-
return this;
|
|
1513
|
-
|
|
1514
|
-
}
|
|
1874
|
+
this.materialLibrary = {};
|
|
1875
|
+
this.materials = [];
|
|
1515
1876
|
|
|
1516
|
-
|
|
1877
|
+
for ( let i = 0, l = materials.length; i < l; i ++ ) {
|
|
1517
1878
|
|
|
1518
|
-
|
|
1519
|
-
const matLib = {};
|
|
1879
|
+
this.addMaterial( materials[ i ] );
|
|
1520
1880
|
|
|
1521
|
-
|
|
1881
|
+
} // Add default main triangle and line edge materials (used in pieces that can be colored with a main color)
|
|
1522
1882
|
|
|
1523
|
-
for ( let i = 0, n = materials.length; i < n; i ++ ) {
|
|
1524
1883
|
|
|
1525
|
-
|
|
1526
|
-
|
|
1884
|
+
this.addMaterial( this.parseColorMetaDirective( new LineParser( 'Main_Colour CODE 16 VALUE #FF8080 EDGE #333333' ) ) );
|
|
1885
|
+
this.addMaterial( this.parseColorMetaDirective( new LineParser( 'Edge_Colour CODE 24 VALUE #A0A0A0 EDGE #333333' ) ) );
|
|
1886
|
+
return this;
|
|
1527
1887
|
|
|
1528
|
-
|
|
1888
|
+
}
|
|
1529
1889
|
|
|
1530
|
-
|
|
1890
|
+
setFileMap( fileMap ) {
|
|
1531
1891
|
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
lib: matLib,
|
|
1535
|
-
url: null,
|
|
1536
|
-
// Subobjects
|
|
1537
|
-
subobjects: null,
|
|
1538
|
-
numSubobjects: 0,
|
|
1539
|
-
subobjectIndex: 0,
|
|
1540
|
-
inverted: false,
|
|
1541
|
-
category: null,
|
|
1542
|
-
keywords: null,
|
|
1543
|
-
// Current subobject
|
|
1544
|
-
currentFileName: null,
|
|
1545
|
-
mainColorCode: parentScope ? parentScope.mainColorCode : '16',
|
|
1546
|
-
mainEdgeColorCode: parentScope ? parentScope.mainEdgeColorCode : '24',
|
|
1547
|
-
matrix: new THREE.Matrix4(),
|
|
1548
|
-
type: 'Model',
|
|
1549
|
-
groupObject: null,
|
|
1550
|
-
// If false, it is a root material scope previous to parse
|
|
1551
|
-
isFromParse: true,
|
|
1552
|
-
faces: [],
|
|
1553
|
-
lineSegments: [],
|
|
1554
|
-
conditionalSegments: [],
|
|
1555
|
-
totalFaces: 0,
|
|
1556
|
-
faceMaterials: new Set(),
|
|
1557
|
-
// If true, this object is the start of a construction step
|
|
1558
|
-
startingConstructionStep: false
|
|
1559
|
-
};
|
|
1560
|
-
return newParseScope;
|
|
1892
|
+
this.fileMap = fileMap;
|
|
1893
|
+
return this;
|
|
1561
1894
|
|
|
1562
1895
|
}
|
|
1563
1896
|
|
|
1564
|
-
addMaterial( material
|
|
1897
|
+
addMaterial( material ) {
|
|
1565
1898
|
|
|
1566
1899
|
// Adds a material to the material library which is on top of the parse scopes stack. And also to the materials array
|
|
1567
|
-
const matLib =
|
|
1900
|
+
const matLib = this.materialLibrary;
|
|
1568
1901
|
|
|
1569
1902
|
if ( ! matLib[ material.userData.code ] ) {
|
|
1570
1903
|
|
|
1571
1904
|
this.materials.push( material );
|
|
1905
|
+
matLib[ material.userData.code ] = material;
|
|
1572
1906
|
|
|
1573
1907
|
}
|
|
1574
1908
|
|
|
1575
|
-
matLib[ material.userData.code ] = material;
|
|
1576
1909
|
return this;
|
|
1577
1910
|
|
|
1578
1911
|
}
|
|
1579
1912
|
|
|
1580
|
-
getMaterial( colorCode
|
|
1913
|
+
getMaterial( colorCode ) {
|
|
1581
1914
|
|
|
1582
|
-
// Given a color code search its material in the parse scopes stack
|
|
1583
1915
|
if ( colorCode.startsWith( '0x2' ) ) {
|
|
1584
1916
|
|
|
1585
1917
|
// Special 'direct' material value (RGB color)
|
|
@@ -1588,24 +1920,117 @@
|
|
|
1588
1920
|
|
|
1589
1921
|
}
|
|
1590
1922
|
|
|
1591
|
-
|
|
1923
|
+
return this.materialLibrary[ colorCode ] || null;
|
|
1924
|
+
|
|
1925
|
+
} // Applies the appropriate materials to a prebuilt hierarchy of geometry. Assumes that color codes are present
|
|
1926
|
+
// in the material array if they need to be filled in.
|
|
1927
|
+
|
|
1928
|
+
|
|
1929
|
+
applyMaterialsToMesh( group, parentColorCode, materialHierarchy, finalMaterialPass = false ) {
|
|
1930
|
+
|
|
1931
|
+
// find any missing materials as indicated by a color code string and replace it with a material from the current material lib
|
|
1932
|
+
const loader = this;
|
|
1933
|
+
const parentIsPassthrough = parentColorCode === MAIN_COLOUR_CODE;
|
|
1934
|
+
group.traverse( c => {
|
|
1935
|
+
|
|
1936
|
+
if ( c.isMesh || c.isLineSegments ) {
|
|
1937
|
+
|
|
1938
|
+
if ( Array.isArray( c.material ) ) {
|
|
1939
|
+
|
|
1940
|
+
for ( let i = 0, l = c.material.length; i < l; i ++ ) {
|
|
1941
|
+
|
|
1942
|
+
if ( ! c.material[ i ].isMaterial ) {
|
|
1943
|
+
|
|
1944
|
+
c.material[ i ] = getMaterial( c, c.material[ i ] );
|
|
1945
|
+
|
|
1946
|
+
}
|
|
1947
|
+
|
|
1948
|
+
}
|
|
1949
|
+
|
|
1950
|
+
} else if ( ! c.material.isMaterial ) {
|
|
1951
|
+
|
|
1952
|
+
c.material = getMaterial( c, c.material );
|
|
1953
|
+
|
|
1954
|
+
}
|
|
1955
|
+
|
|
1956
|
+
}
|
|
1957
|
+
|
|
1958
|
+
} ); // Returns the appropriate material for the object (line or face) given color code. If the code is "pass through"
|
|
1959
|
+
// (24 for lines, 16 for edges) then the pass through color code is used. If that is also pass through then it's
|
|
1960
|
+
// simply returned for the subsequent material application.
|
|
1592
1961
|
|
|
1593
|
-
|
|
1962
|
+
function getMaterial( c, colorCode ) {
|
|
1594
1963
|
|
|
1595
|
-
if
|
|
1964
|
+
// if our parent is a passthrough color code and we don't have the current material color available then
|
|
1965
|
+
// return early.
|
|
1966
|
+
if ( parentIsPassthrough && ! ( colorCode in materialHierarchy ) && ! finalMaterialPass ) {
|
|
1596
1967
|
|
|
1597
|
-
return
|
|
1968
|
+
return colorCode;
|
|
1969
|
+
|
|
1970
|
+
}
|
|
1971
|
+
|
|
1972
|
+
const forEdge = c.isLineSegments || c.isConditionalLine;
|
|
1973
|
+
const isPassthrough = ! forEdge && colorCode === MAIN_COLOUR_CODE || forEdge && colorCode === MAIN_EDGE_COLOUR_CODE;
|
|
1974
|
+
|
|
1975
|
+
if ( isPassthrough ) {
|
|
1976
|
+
|
|
1977
|
+
colorCode = parentColorCode;
|
|
1978
|
+
|
|
1979
|
+
}
|
|
1980
|
+
|
|
1981
|
+
let material = null;
|
|
1982
|
+
|
|
1983
|
+
if ( colorCode in materialHierarchy ) {
|
|
1984
|
+
|
|
1985
|
+
material = materialHierarchy[ colorCode ];
|
|
1986
|
+
|
|
1987
|
+
} else if ( finalMaterialPass ) {
|
|
1988
|
+
|
|
1989
|
+
// see if we can get the final material from from the "getMaterial" function which will attempt to
|
|
1990
|
+
// parse the "direct" colors
|
|
1991
|
+
material = loader.getMaterial( colorCode );
|
|
1992
|
+
|
|
1993
|
+
if ( material === null ) {
|
|
1994
|
+
|
|
1995
|
+
// otherwise throw an error if this is final opportunity to set the material
|
|
1996
|
+
throw new Error( `LDrawLoader: Material properties for code ${colorCode} not available.` );
|
|
1997
|
+
|
|
1998
|
+
}
|
|
1598
1999
|
|
|
1599
2000
|
} else {
|
|
1600
2001
|
|
|
1601
|
-
|
|
2002
|
+
return colorCode;
|
|
1602
2003
|
|
|
1603
2004
|
}
|
|
1604
2005
|
|
|
1605
|
-
|
|
2006
|
+
if ( c.isLineSegments ) {
|
|
2007
|
+
|
|
2008
|
+
material = material.userData.edgeMaterial;
|
|
2009
|
+
|
|
2010
|
+
if ( c.isConditionalLine ) {
|
|
1606
2011
|
|
|
2012
|
+
material = material.userData.conditionalEdgeMaterial;
|
|
1607
2013
|
|
|
1608
|
-
|
|
2014
|
+
}
|
|
2015
|
+
|
|
2016
|
+
}
|
|
2017
|
+
|
|
2018
|
+
return material;
|
|
2019
|
+
|
|
2020
|
+
}
|
|
2021
|
+
|
|
2022
|
+
}
|
|
2023
|
+
|
|
2024
|
+
getMainMaterial() {
|
|
2025
|
+
|
|
2026
|
+
return this.getMaterial( MAIN_COLOUR_CODE );
|
|
2027
|
+
|
|
2028
|
+
}
|
|
2029
|
+
|
|
2030
|
+
getMainEdgeMaterial() {
|
|
2031
|
+
|
|
2032
|
+
const mainMat = this.getMainMaterial();
|
|
2033
|
+
return mainMat && mainMat.userData ? mainMat.userData.edgeMaterial : null;
|
|
1609
2034
|
|
|
1610
2035
|
}
|
|
1611
2036
|
|
|
@@ -1820,6 +2245,7 @@
|
|
|
1820
2245
|
material.premultipliedAlpha = true;
|
|
1821
2246
|
material.opacity = alpha;
|
|
1822
2247
|
material.depthWrite = ! isTransparent;
|
|
2248
|
+
material.color.convertSRGBToLinear();
|
|
1823
2249
|
material.polygonOffset = true;
|
|
1824
2250
|
material.polygonOffsetFactor = 1;
|
|
1825
2251
|
|
|
@@ -1839,7 +2265,8 @@
|
|
|
1839
2265
|
depthWrite: ! isTransparent
|
|
1840
2266
|
} );
|
|
1841
2267
|
edgeMaterial.userData.code = code;
|
|
1842
|
-
edgeMaterial.name = name + ' - Edge';
|
|
2268
|
+
edgeMaterial.name = name + ' - Edge';
|
|
2269
|
+
edgeMaterial.color.convertSRGBToLinear(); // This is the material used for conditional edges
|
|
1843
2270
|
|
|
1844
2271
|
edgeMaterial.userData.conditionalEdgeMaterial = new LDrawConditionalLineMaterial( {
|
|
1845
2272
|
fog: true,
|
|
@@ -1848,125 +2275,16 @@
|
|
|
1848
2275
|
color: edgeColor,
|
|
1849
2276
|
opacity: alpha
|
|
1850
2277
|
} );
|
|
2278
|
+
edgeMaterial.userData.conditionalEdgeMaterial.color.convertSRGBToLinear();
|
|
1851
2279
|
|
|
1852
2280
|
}
|
|
1853
2281
|
|
|
1854
2282
|
material.userData.code = code;
|
|
1855
2283
|
material.name = name;
|
|
1856
2284
|
material.userData.edgeMaterial = edgeMaterial;
|
|
2285
|
+
this.addMaterial( material );
|
|
1857
2286
|
return material;
|
|
1858
2287
|
|
|
1859
|
-
} //
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
objectParse( info, parseScope ) {
|
|
1863
|
-
|
|
1864
|
-
// Retrieve data from the parent parse scope
|
|
1865
|
-
const currentParseScope = parseScope;
|
|
1866
|
-
const parentParseScope = currentParseScope.parentScope; // Main color codes passed to this subobject (or default codes 16 and 24 if it is the root object)
|
|
1867
|
-
|
|
1868
|
-
const mainColorCode = currentParseScope.mainColorCode;
|
|
1869
|
-
const mainEdgeColorCode = currentParseScope.mainEdgeColorCode;
|
|
1870
|
-
|
|
1871
|
-
const parseColorCode = ( colorCode, forEdge ) => {
|
|
1872
|
-
|
|
1873
|
-
// Parses next color code and returns a THREE.Material
|
|
1874
|
-
if ( ! forEdge && colorCode === '16' ) {
|
|
1875
|
-
|
|
1876
|
-
colorCode = mainColorCode;
|
|
1877
|
-
|
|
1878
|
-
}
|
|
1879
|
-
|
|
1880
|
-
if ( forEdge && colorCode === '24' ) {
|
|
1881
|
-
|
|
1882
|
-
colorCode = mainEdgeColorCode;
|
|
1883
|
-
|
|
1884
|
-
}
|
|
1885
|
-
|
|
1886
|
-
const material = this.getMaterial( colorCode, currentParseScope );
|
|
1887
|
-
|
|
1888
|
-
if ( ! material ) {
|
|
1889
|
-
|
|
1890
|
-
throw new Error( 'LDrawLoader: Unknown color code "' + colorCode + '" is used but it was not defined previously.' );
|
|
1891
|
-
|
|
1892
|
-
}
|
|
1893
|
-
|
|
1894
|
-
return material;
|
|
1895
|
-
|
|
1896
|
-
};
|
|
1897
|
-
|
|
1898
|
-
const faces = info.faces;
|
|
1899
|
-
const lineSegments = info.lineSegments;
|
|
1900
|
-
const conditionalSegments = info.conditionalSegments;
|
|
1901
|
-
const materials = info.materials;
|
|
1902
|
-
|
|
1903
|
-
if ( currentParseScope.inverted ) {
|
|
1904
|
-
|
|
1905
|
-
faces.reverse();
|
|
1906
|
-
|
|
1907
|
-
}
|
|
1908
|
-
|
|
1909
|
-
for ( const colorCode in materials ) {
|
|
1910
|
-
|
|
1911
|
-
this.addMaterial( materials[ colorCode ], currentParseScope );
|
|
1912
|
-
|
|
1913
|
-
}
|
|
1914
|
-
|
|
1915
|
-
for ( let i = 0, l = faces.length; i < l; i ++ ) {
|
|
1916
|
-
|
|
1917
|
-
const face = faces[ i ];
|
|
1918
|
-
|
|
1919
|
-
if ( face.material === null ) {
|
|
1920
|
-
|
|
1921
|
-
face.material = parseColorCode( face.colorCode, false );
|
|
1922
|
-
|
|
1923
|
-
}
|
|
1924
|
-
|
|
1925
|
-
}
|
|
1926
|
-
|
|
1927
|
-
for ( let i = 0, l = lineSegments.length; i < l; i ++ ) {
|
|
1928
|
-
|
|
1929
|
-
const ls = lineSegments[ i ];
|
|
1930
|
-
|
|
1931
|
-
if ( ls.material === null ) {
|
|
1932
|
-
|
|
1933
|
-
ls.material = parseColorCode( ls.colorCode, true );
|
|
1934
|
-
|
|
1935
|
-
}
|
|
1936
|
-
|
|
1937
|
-
}
|
|
1938
|
-
|
|
1939
|
-
for ( let i = 0, l = conditionalSegments.length; i < l; i ++ ) {
|
|
1940
|
-
|
|
1941
|
-
const cs = conditionalSegments[ i ];
|
|
1942
|
-
|
|
1943
|
-
if ( cs.material === null ) {
|
|
1944
|
-
|
|
1945
|
-
cs.material = parseColorCode( cs.colorCode, true );
|
|
1946
|
-
|
|
1947
|
-
}
|
|
1948
|
-
|
|
1949
|
-
}
|
|
1950
|
-
|
|
1951
|
-
currentParseScope.faces = info.faces;
|
|
1952
|
-
currentParseScope.conditionalSegments = info.conditionalSegments;
|
|
1953
|
-
currentParseScope.lineSegments = info.lineSegments;
|
|
1954
|
-
currentParseScope.category = info.category;
|
|
1955
|
-
currentParseScope.keywords = info.keywords;
|
|
1956
|
-
currentParseScope.subobjects = info.subobjects;
|
|
1957
|
-
currentParseScope.numSubobjects = info.subobjects.length;
|
|
1958
|
-
currentParseScope.subobjectIndex = 0;
|
|
1959
|
-
currentParseScope.type = info.type;
|
|
1960
|
-
currentParseScope.totalFaces = info.totalFaces;
|
|
1961
|
-
const isRoot = ! parentParseScope.isFromParse;
|
|
1962
|
-
|
|
1963
|
-
if ( isRoot || ! isPrimitiveType( info.type ) ) {
|
|
1964
|
-
|
|
1965
|
-
currentParseScope.groupObject = new THREE.Group();
|
|
1966
|
-
currentParseScope.groupObject.userData.startingConstructionStep = currentParseScope.startingConstructionStep;
|
|
1967
|
-
|
|
1968
|
-
}
|
|
1969
|
-
|
|
1970
2288
|
}
|
|
1971
2289
|
|
|
1972
2290
|
computeConstructionSteps( model ) {
|
|
@@ -1992,208 +2310,6 @@
|
|
|
1992
2310
|
|
|
1993
2311
|
}
|
|
1994
2312
|
|
|
1995
|
-
finalizeObject( subobjectParseScope ) {
|
|
1996
|
-
|
|
1997
|
-
// fail gracefully if an object could not be loaded
|
|
1998
|
-
if ( subobjectParseScope === null ) {
|
|
1999
|
-
|
|
2000
|
-
return;
|
|
2001
|
-
|
|
2002
|
-
}
|
|
2003
|
-
|
|
2004
|
-
const parentParseScope = subobjectParseScope.parentScope; // Smooth the normals if this is a part or if this is a case where the subpart
|
|
2005
|
-
// is added directly into the parent model (meaning it will never get smoothed by
|
|
2006
|
-
// being added to a part)
|
|
2007
|
-
|
|
2008
|
-
const doSmooth = isPartType( subobjectParseScope.type ) || isPrimitiveType( subobjectParseScope.type ) && isModelType( subobjectParseScope.parentScope.type );
|
|
2009
|
-
|
|
2010
|
-
if ( this.smoothNormals && doSmooth ) {
|
|
2011
|
-
|
|
2012
|
-
generateFaceNormals( subobjectParseScope.faces ); // only check subsetgments if we have multiple materials in a single part because this seems to be the case where it's needed most --
|
|
2013
|
-
// there may be cases where a single edge line crosses over polygon edges that are broken up by multiple materials.
|
|
2014
|
-
|
|
2015
|
-
const checkSubSegments = subobjectParseScope.faceMaterials.size > 1;
|
|
2016
|
-
smoothNormals( subobjectParseScope.faces, subobjectParseScope.lineSegments, checkSubSegments );
|
|
2017
|
-
|
|
2018
|
-
}
|
|
2019
|
-
|
|
2020
|
-
const isRoot = ! parentParseScope.isFromParse;
|
|
2021
|
-
|
|
2022
|
-
if ( ! isPrimitiveType( subobjectParseScope.type ) || isRoot ) {
|
|
2023
|
-
|
|
2024
|
-
const objGroup = subobjectParseScope.groupObject;
|
|
2025
|
-
|
|
2026
|
-
if ( subobjectParseScope.faces.length > 0 ) {
|
|
2027
|
-
|
|
2028
|
-
objGroup.add( createObject( subobjectParseScope.faces, 3, false, subobjectParseScope.totalFaces ) );
|
|
2029
|
-
|
|
2030
|
-
}
|
|
2031
|
-
|
|
2032
|
-
if ( subobjectParseScope.lineSegments.length > 0 ) {
|
|
2033
|
-
|
|
2034
|
-
objGroup.add( createObject( subobjectParseScope.lineSegments, 2 ) );
|
|
2035
|
-
|
|
2036
|
-
}
|
|
2037
|
-
|
|
2038
|
-
if ( subobjectParseScope.conditionalSegments.length > 0 ) {
|
|
2039
|
-
|
|
2040
|
-
objGroup.add( createObject( subobjectParseScope.conditionalSegments, 2, true ) );
|
|
2041
|
-
|
|
2042
|
-
}
|
|
2043
|
-
|
|
2044
|
-
if ( parentParseScope.groupObject ) {
|
|
2045
|
-
|
|
2046
|
-
objGroup.name = subobjectParseScope.fileName;
|
|
2047
|
-
objGroup.userData.category = subobjectParseScope.category;
|
|
2048
|
-
objGroup.userData.keywords = subobjectParseScope.keywords;
|
|
2049
|
-
subobjectParseScope.matrix.decompose( objGroup.position, objGroup.quaternion, objGroup.scale );
|
|
2050
|
-
parentParseScope.groupObject.add( objGroup );
|
|
2051
|
-
|
|
2052
|
-
}
|
|
2053
|
-
|
|
2054
|
-
} else {
|
|
2055
|
-
|
|
2056
|
-
const parentLineSegments = parentParseScope.lineSegments;
|
|
2057
|
-
const parentConditionalSegments = parentParseScope.conditionalSegments;
|
|
2058
|
-
const parentFaces = parentParseScope.faces;
|
|
2059
|
-
const parentFaceMaterials = parentParseScope.faceMaterials;
|
|
2060
|
-
const lineSegments = subobjectParseScope.lineSegments;
|
|
2061
|
-
const conditionalSegments = subobjectParseScope.conditionalSegments;
|
|
2062
|
-
const faces = subobjectParseScope.faces;
|
|
2063
|
-
const faceMaterials = subobjectParseScope.faceMaterials;
|
|
2064
|
-
const matrix = subobjectParseScope.matrix;
|
|
2065
|
-
const matrixScaleInverted = matrix.determinant() < 0;
|
|
2066
|
-
|
|
2067
|
-
for ( let i = 0, l = lineSegments.length; i < l; i ++ ) {
|
|
2068
|
-
|
|
2069
|
-
const ls = lineSegments[ i ];
|
|
2070
|
-
const vertices = ls.vertices;
|
|
2071
|
-
vertices[ 0 ].applyMatrix4( matrix );
|
|
2072
|
-
vertices[ 1 ].applyMatrix4( matrix );
|
|
2073
|
-
parentLineSegments.push( ls );
|
|
2074
|
-
|
|
2075
|
-
}
|
|
2076
|
-
|
|
2077
|
-
for ( let i = 0, l = conditionalSegments.length; i < l; i ++ ) {
|
|
2078
|
-
|
|
2079
|
-
const os = conditionalSegments[ i ];
|
|
2080
|
-
const vertices = os.vertices;
|
|
2081
|
-
const controlPoints = os.controlPoints;
|
|
2082
|
-
vertices[ 0 ].applyMatrix4( matrix );
|
|
2083
|
-
vertices[ 1 ].applyMatrix4( matrix );
|
|
2084
|
-
controlPoints[ 0 ].applyMatrix4( matrix );
|
|
2085
|
-
controlPoints[ 1 ].applyMatrix4( matrix );
|
|
2086
|
-
parentConditionalSegments.push( os );
|
|
2087
|
-
|
|
2088
|
-
}
|
|
2089
|
-
|
|
2090
|
-
for ( let i = 0, l = faces.length; i < l; i ++ ) {
|
|
2091
|
-
|
|
2092
|
-
const tri = faces[ i ];
|
|
2093
|
-
const vertices = tri.vertices;
|
|
2094
|
-
|
|
2095
|
-
for ( let i = 0, l = vertices.length; i < l; i ++ ) {
|
|
2096
|
-
|
|
2097
|
-
vertices[ i ].applyMatrix4( matrix );
|
|
2098
|
-
|
|
2099
|
-
} // If the scale of the object is negated then the triangle winding order
|
|
2100
|
-
// needs to be flipped.
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
if ( matrixScaleInverted !== subobjectParseScope.inverted ) {
|
|
2104
|
-
|
|
2105
|
-
vertices.reverse();
|
|
2106
|
-
|
|
2107
|
-
}
|
|
2108
|
-
|
|
2109
|
-
parentFaces.push( tri );
|
|
2110
|
-
|
|
2111
|
-
}
|
|
2112
|
-
|
|
2113
|
-
parentParseScope.totalFaces += subobjectParseScope.totalFaces;
|
|
2114
|
-
faceMaterials.forEach( material => parentFaceMaterials.add( material ) );
|
|
2115
|
-
|
|
2116
|
-
}
|
|
2117
|
-
|
|
2118
|
-
}
|
|
2119
|
-
|
|
2120
|
-
async processObject( parsedInfo, subobject, url, parentScope ) {
|
|
2121
|
-
|
|
2122
|
-
const scope = this;
|
|
2123
|
-
const parseScope = this.newParseScopeLevel( null, parentScope );
|
|
2124
|
-
parseScope.url = url;
|
|
2125
|
-
const parentParseScope = parseScope.parentScope; // Set current matrix
|
|
2126
|
-
|
|
2127
|
-
if ( subobject ) {
|
|
2128
|
-
|
|
2129
|
-
parseScope.matrix.copy( subobject.matrix );
|
|
2130
|
-
parseScope.inverted = subobject.inverted;
|
|
2131
|
-
parseScope.startingConstructionStep = subobject.startingConstructionStep;
|
|
2132
|
-
parseScope.fileName = subobject.fileName;
|
|
2133
|
-
|
|
2134
|
-
if ( subobject.colorCode === '16' && parseScope.parentScope ) {
|
|
2135
|
-
|
|
2136
|
-
const parentScope = parseScope.parentScope;
|
|
2137
|
-
parseScope.mainColorCode = parentScope.mainColorCode;
|
|
2138
|
-
parseScope.mainEdgeColorCode = parentScope.mainEdgeColorCode;
|
|
2139
|
-
|
|
2140
|
-
} else if ( subobject.colorCode !== '16' ) {
|
|
2141
|
-
|
|
2142
|
-
parseScope.mainColorCode = subobject.colorCode;
|
|
2143
|
-
parseScope.mainEdgeColorCode = subobject.colorCode;
|
|
2144
|
-
|
|
2145
|
-
}
|
|
2146
|
-
|
|
2147
|
-
} // Parse the object
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
this.objectParse( parsedInfo, parseScope );
|
|
2151
|
-
const subobjects = parseScope.subobjects;
|
|
2152
|
-
const promises = [];
|
|
2153
|
-
|
|
2154
|
-
for ( let i = 0, l = subobjects.length; i < l; i ++ ) {
|
|
2155
|
-
|
|
2156
|
-
promises.push( loadSubobject( parseScope.subobjects[ i ] ) );
|
|
2157
|
-
|
|
2158
|
-
} // Kick off of the downloads in parallel but process all the subobjects
|
|
2159
|
-
// in order so all the assembly instructions are correct
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
const subobjectScopes = await Promise.all( promises );
|
|
2163
|
-
|
|
2164
|
-
for ( let i = 0, l = subobjectScopes.length; i < l; i ++ ) {
|
|
2165
|
-
|
|
2166
|
-
this.finalizeObject( subobjectScopes[ i ] );
|
|
2167
|
-
|
|
2168
|
-
} // If it is root object then finalize this object and compute construction steps
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
if ( ! parentParseScope.isFromParse ) {
|
|
2172
|
-
|
|
2173
|
-
this.finalizeObject( parseScope );
|
|
2174
|
-
this.computeConstructionSteps( parseScope.groupObject );
|
|
2175
|
-
|
|
2176
|
-
}
|
|
2177
|
-
|
|
2178
|
-
return parseScope;
|
|
2179
|
-
|
|
2180
|
-
function loadSubobject( subobject ) {
|
|
2181
|
-
|
|
2182
|
-
return scope.parseCache.loadData( subobject.fileName ).then( function ( parsedInfo ) {
|
|
2183
|
-
|
|
2184
|
-
return scope.processObject( parsedInfo, subobject, url, parseScope );
|
|
2185
|
-
|
|
2186
|
-
} ).catch( function ( err ) {
|
|
2187
|
-
|
|
2188
|
-
console.warn( err );
|
|
2189
|
-
return null;
|
|
2190
|
-
|
|
2191
|
-
} );
|
|
2192
|
-
|
|
2193
|
-
}
|
|
2194
|
-
|
|
2195
|
-
}
|
|
2196
|
-
|
|
2197
2313
|
}
|
|
2198
2314
|
|
|
2199
2315
|
THREE.LDrawLoader = LDrawLoader;
|