super-three 0.136.0 → 0.136.1
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/LICENSE +1 -1
- package/build/three.js +1634 -1608
- package/build/three.min.js +1 -1
- package/build/three.module.js +4630 -4534
- package/examples/js/animation/MMDPhysics.js +1 -1
- package/examples/js/cameras/CinematicCamera.js +2 -7
- package/examples/js/exporters/MMDExporter.js +1 -1
- package/examples/js/geometries/ConvexGeometry.js +1 -1
- package/examples/js/loaders/BasisTextureLoader.js +1 -0
- package/examples/js/loaders/EXRLoader.js +25 -19
- package/examples/js/loaders/GLTFLoader.js +2 -2
- package/examples/js/loaders/HDRCubeTextureLoader.js +0 -2
- package/examples/js/loaders/LDrawLoader.js +1131 -859
- package/examples/js/loaders/LUT3dlLoader.js +17 -10
- package/examples/js/loaders/LUTCubeLoader.js +3 -4
- package/examples/js/loaders/RGBELoader.js +6 -6
- package/examples/js/loaders/RGBMLoader.js +1 -1
- package/examples/js/loaders/SVGLoader.js +3 -1
- package/examples/js/loaders/VRMLLoader.js +9 -21
- package/examples/js/math/ConvexHull.js +7 -13
- package/examples/js/misc/Volume.js +2 -2
- package/examples/js/modifiers/CurveModifier.js +8 -7
- package/examples/js/modifiers/EdgeSplitModifier.js +1 -1
- package/examples/js/modifiers/SimplifyModifier.js +1 -1
- package/examples/js/objects/Reflector.js +1 -13
- package/examples/js/objects/ReflectorForSSRPass.js +0 -10
- package/examples/js/objects/Refractor.js +1 -13
- package/examples/js/objects/Water.js +1 -13
- package/examples/js/postprocessing/GlitchPass.js +4 -5
- package/examples/js/postprocessing/SMAAPass.js +2 -9
- package/examples/js/postprocessing/SSAOPass.js +9 -21
- package/examples/js/postprocessing/SavePass.js +1 -5
- package/examples/js/shaders/DigitalGlitch.js +3 -3
- package/examples/js/shaders/MMDToonShader.js +0 -1
- package/examples/js/shaders/SSAOShader.js +1 -1
- package/examples/jsm/animation/MMDPhysics.js +1 -1
- package/examples/jsm/cameras/CinematicCamera.js +2 -5
- package/examples/jsm/{WebGL.js → capabilities/WebGL.js} +2 -2
- package/examples/jsm/{renderers/webgpu → capabilities}/WebGPU.js +1 -1
- package/examples/jsm/exporters/GLTFExporter.js +8 -24
- package/examples/jsm/exporters/MMDExporter.js +1 -1
- package/examples/jsm/geometries/ConvexGeometry.js +1 -1
- package/examples/jsm/libs/lil-gui.module.min.js +2 -2
- package/examples/jsm/loaders/BasisTextureLoader.js +7 -0
- package/examples/jsm/loaders/EXRLoader.js +22 -19
- package/examples/jsm/loaders/GLTFLoader.js +1 -3
- package/examples/jsm/loaders/HDRCubeTextureLoader.js +1 -4
- package/examples/jsm/loaders/LDrawLoader.js +1147 -885
- package/examples/jsm/loaders/LUT3dlLoader.js +17 -12
- package/examples/jsm/loaders/LUTCubeLoader.js +3 -5
- package/examples/jsm/loaders/RGBELoader.js +7 -8
- package/examples/jsm/loaders/RGBMLoader.js +1 -1
- package/examples/jsm/loaders/SVGLoader.js +6 -1
- package/examples/jsm/loaders/VRMLLoader.js +9 -22
- package/examples/jsm/math/ConvexHull.js +7 -13
- package/examples/jsm/misc/Volume.js +2 -2
- package/examples/jsm/modifiers/CurveModifier.js +9 -8
- package/examples/jsm/modifiers/EdgeSplitModifier.js +1 -1
- package/examples/jsm/modifiers/SimplifyModifier.js +1 -1
- package/examples/jsm/node-editor/NodeEditor.js +21 -22
- package/examples/jsm/nodes/utils/ColorSpaceNode.js +4 -13
- package/examples/jsm/objects/Lensflare.js +4 -4
- package/examples/jsm/objects/Reflector.js +1 -16
- package/examples/jsm/objects/ReflectorForSSRPass.js +0 -12
- package/examples/jsm/objects/Refractor.js +1 -16
- package/examples/jsm/objects/Water.js +1 -16
- package/examples/jsm/postprocessing/GlitchPass.js +7 -6
- package/examples/jsm/postprocessing/SMAAPass.js +2 -11
- package/examples/jsm/postprocessing/SSAOPass.js +13 -25
- package/examples/jsm/postprocessing/SavePass.js +1 -3
- package/examples/jsm/renderers/nodes/Nodes.js +2 -0
- package/examples/jsm/renderers/nodes/ShaderNode.js +58 -31
- package/examples/jsm/renderers/nodes/display/ColorSpaceNode.js +9 -46
- package/examples/jsm/renderers/nodes/inputs/TextureNode.js +5 -10
- package/examples/jsm/renderers/nodes/math/MathNode.js +2 -1
- package/examples/jsm/renderers/webgpu/WebGPUObjects.js +1 -1
- package/examples/jsm/renderers/webgpu/WebGPURenderer.js +3 -5
- package/examples/jsm/renderers/webgpu/WebGPUTextures.js +1 -2
- package/examples/jsm/renderers/webgpu/nodes/WebGPUNodeBuilder.js +10 -4
- package/examples/jsm/shaders/DigitalGlitch.js +3 -3
- package/examples/jsm/shaders/MMDToonShader.js +0 -1
- package/examples/jsm/shaders/SSAOShader.js +1 -1
- package/examples/jsm/utils/LDrawUtils.js +195 -0
- package/examples/jsm/webxr/VRButton.js +24 -0
- package/examples/jsm/webxr/XREstimatedLight.js +2 -0
- package/package.json +4 -1
- package/src/animation/AnimationMixer.js +1 -0
- package/src/cameras/CubeCamera.js +2 -0
- package/src/constants.js +3 -16
- package/src/extras/ImageUtils.js +63 -0
- package/src/extras/PMREMGenerator.js +32 -82
- package/src/loaders/MaterialLoader.js +1 -1
- package/src/materials/Material.js +4 -4
- package/src/math/Color.js +1 -1
- package/src/math/Quaternion.js +1 -1
- package/src/renderers/WebGLCubeRenderTarget.js +0 -2
- package/src/renderers/WebGLRenderer.js +1 -67
- package/src/renderers/shaders/ShaderChunk/emissivemap_fragment.glsl.js +0 -2
- package/src/renderers/shaders/ShaderChunk/encodings_pars_fragment.glsl.js +0 -4
- package/src/renderers/shaders/ShaderChunk/envmap_fragment.glsl.js +0 -2
- package/src/renderers/shaders/ShaderChunk/lightmap_fragment.glsl.js +1 -1
- package/src/renderers/shaders/ShaderChunk/lights_fragment_maps.glsl.js +1 -1
- package/src/renderers/shaders/ShaderChunk/lights_physical_fragment.glsl.js +2 -2
- package/src/renderers/shaders/ShaderChunk/map_fragment.glsl.js +10 -3
- package/src/renderers/shaders/ShaderChunk/map_particle_fragment.glsl.js +1 -2
- package/src/renderers/shaders/ShaderChunk/transmission_pars_fragment.glsl.js +8 -7
- package/src/renderers/shaders/ShaderLib/background.glsl.js +1 -3
- package/src/renderers/shaders/ShaderLib/equirect.glsl.js +1 -3
- package/src/renderers/shaders/ShaderLib/meshbasic.glsl.js +1 -1
- package/src/renderers/shaders/ShaderLib/meshmatcap.glsl.js +1 -2
- package/src/renderers/shaders/ShaderLib.js +2 -2
- package/src/renderers/webgl/WebGLCubeMaps.js +0 -4
- package/src/renderers/webgl/WebGLCubeUVMaps.js +29 -16
- package/src/renderers/webgl/WebGLMorphtargets.js +12 -0
- package/src/renderers/webgl/WebGLProgram.js +4 -16
- package/src/renderers/webgl/WebGLPrograms.js +8 -47
- package/src/renderers/webgl/WebGLState.js +81 -0
- package/src/renderers/webgl/WebGLTextures.js +75 -22
- package/src/renderers/webgl/WebGLUtils.js +86 -28
- package/src/renderers/webxr/WebXRManager.js +3 -3
- package/src/textures/Texture.js +2 -1
- package/src/textures/VideoTexture.js +1 -3
- package/examples/js/WebGL.js +0 -90
- package/examples/js/utils/RoughnessMipmapper.js +0 -268
- package/examples/jsm/utils/RoughnessMipmapper.js +0 -288
|
@@ -142,16 +142,45 @@
|
|
|
142
142
|
|
|
143
143
|
}
|
|
144
144
|
|
|
145
|
-
function
|
|
145
|
+
function generateFaceNormals( faces ) {
|
|
146
|
+
|
|
147
|
+
for ( let i = 0, l = faces.length; i < l; i ++ ) {
|
|
148
|
+
|
|
149
|
+
const face = faces[ i ];
|
|
150
|
+
const vertices = face.vertices;
|
|
151
|
+
const v0 = vertices[ 0 ];
|
|
152
|
+
const v1 = vertices[ 1 ];
|
|
153
|
+
const v2 = vertices[ 2 ];
|
|
154
|
+
|
|
155
|
+
_tempVec0.subVectors( v1, v0 );
|
|
156
|
+
|
|
157
|
+
_tempVec1.subVectors( v2, v1 );
|
|
158
|
+
|
|
159
|
+
face.faceNormal = new THREE.Vector3().crossVectors( _tempVec0, _tempVec1 ).normalize();
|
|
160
|
+
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const _ray = new THREE.Ray();
|
|
166
|
+
|
|
167
|
+
function smoothNormals( faces, lineSegments, checkSubSegments = false ) {
|
|
168
|
+
|
|
169
|
+
// NOTE: 1e2 is pretty coarse but was chosen to quantize the resulting value because
|
|
170
|
+
// it allows edges to be smoothed as expected (see minifig arms).
|
|
171
|
+
// --
|
|
172
|
+
// And the vector values are initialize multiplied by 1 + 1e-10 to account for floating
|
|
173
|
+
// point errors on vertices along quantization boundaries. Ie after matrix multiplication
|
|
174
|
+
// vertices that should be merged might be set to "1.7" and "1.6999..." meaning they won't
|
|
175
|
+
// get merged. This added epsilon attempts to push these error values to the same quantized
|
|
176
|
+
// value for the sake of hashing. See "AT-ST mini" dishes. See mrdoob/three#23169.
|
|
177
|
+
const hashMultiplier = ( 1 + 1e-10 ) * 1e2;
|
|
146
178
|
|
|
147
179
|
function hashVertex( v ) {
|
|
148
180
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
const x = ~ ~ ( v.x * 1e2 );
|
|
153
|
-
const y = ~ ~ ( v.y * 1e2 );
|
|
154
|
-
const z = ~ ~ ( v.z * 1e2 );
|
|
181
|
+
const x = ~ ~ ( v.x * hashMultiplier );
|
|
182
|
+
const y = ~ ~ ( v.y * hashMultiplier );
|
|
183
|
+
const z = ~ ~ ( v.z * hashMultiplier );
|
|
155
184
|
return `${x},${y},${z}`;
|
|
156
185
|
|
|
157
186
|
}
|
|
@@ -160,9 +189,27 @@
|
|
|
160
189
|
|
|
161
190
|
return `${hashVertex( v0 )}_${hashVertex( v1 )}`;
|
|
162
191
|
|
|
192
|
+
} // converts the two vertices to a ray with a normalized direction and origin of 0, 0, 0 projected
|
|
193
|
+
// onto the original line.
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
function toNormalizedRay( v0, v1, targetRay ) {
|
|
197
|
+
|
|
198
|
+
targetRay.direction.subVectors( v1, v0 ).normalize();
|
|
199
|
+
const scalar = v0.dot( targetRay.direction );
|
|
200
|
+
targetRay.origin.copy( v0 ).addScaledVector( targetRay.direction, - scalar );
|
|
201
|
+
return targetRay;
|
|
202
|
+
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function hashRay( ray ) {
|
|
206
|
+
|
|
207
|
+
return hashEdge( ray.origin, ray.direction );
|
|
208
|
+
|
|
163
209
|
}
|
|
164
210
|
|
|
165
211
|
const hardEdges = new Set();
|
|
212
|
+
const hardEdgeRays = new Map();
|
|
166
213
|
const halfEdgeList = {};
|
|
167
214
|
const normals = []; // Save the list of hard edges by hash
|
|
168
215
|
|
|
@@ -173,7 +220,43 @@
|
|
|
173
220
|
const v0 = vertices[ 0 ];
|
|
174
221
|
const v1 = vertices[ 1 ];
|
|
175
222
|
hardEdges.add( hashEdge( v0, v1 ) );
|
|
176
|
-
hardEdges.add( hashEdge( v1, v0 ) );
|
|
223
|
+
hardEdges.add( hashEdge( v1, v0 ) ); // only generate the hard edge ray map if we're checking subsegments because it's more expensive to check
|
|
224
|
+
// and requires more memory.
|
|
225
|
+
|
|
226
|
+
if ( checkSubSegments ) {
|
|
227
|
+
|
|
228
|
+
// add both ray directions to the map
|
|
229
|
+
const ray = toNormalizedRay( v0, v1, new THREE.Ray() );
|
|
230
|
+
const rh1 = hashRay( ray );
|
|
231
|
+
|
|
232
|
+
if ( ! hardEdgeRays.has( rh1 ) ) {
|
|
233
|
+
|
|
234
|
+
toNormalizedRay( v1, v0, ray );
|
|
235
|
+
const rh2 = hashRay( ray );
|
|
236
|
+
const info = {
|
|
237
|
+
ray,
|
|
238
|
+
distances: []
|
|
239
|
+
};
|
|
240
|
+
hardEdgeRays.set( rh1, info );
|
|
241
|
+
hardEdgeRays.set( rh2, info );
|
|
242
|
+
|
|
243
|
+
} // store both segments ends in min, max order in the distances array to check if a face edge is a
|
|
244
|
+
// subsegment later.
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
const info = hardEdgeRays.get( rh1 );
|
|
248
|
+
let d0 = info.ray.direction.dot( v0 );
|
|
249
|
+
let d1 = info.ray.direction.dot( v1 );
|
|
250
|
+
|
|
251
|
+
if ( d0 > d1 ) {
|
|
252
|
+
|
|
253
|
+
[ d0, d1 ] = [ d1, d0 ];
|
|
254
|
+
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
info.distances.push( d0, d1 );
|
|
258
|
+
|
|
259
|
+
}
|
|
177
260
|
|
|
178
261
|
} // track the half edges associated with each triangle
|
|
179
262
|
|
|
@@ -192,7 +275,58 @@
|
|
|
192
275
|
const v1 = vertices[ next ];
|
|
193
276
|
const hash = hashEdge( v0, v1 ); // don't add the triangle if the edge is supposed to be hard
|
|
194
277
|
|
|
195
|
-
if ( hardEdges.has( hash ) )
|
|
278
|
+
if ( hardEdges.has( hash ) ) {
|
|
279
|
+
|
|
280
|
+
continue;
|
|
281
|
+
|
|
282
|
+
} // if checking subsegments then check to see if this edge lies on a hard edge ray and whether its within any ray bounds
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
if ( checkSubSegments ) {
|
|
286
|
+
|
|
287
|
+
toNormalizedRay( v0, v1, _ray );
|
|
288
|
+
const rayHash = hashRay( _ray );
|
|
289
|
+
|
|
290
|
+
if ( hardEdgeRays.has( rayHash ) ) {
|
|
291
|
+
|
|
292
|
+
const info = hardEdgeRays.get( rayHash );
|
|
293
|
+
const {
|
|
294
|
+
ray,
|
|
295
|
+
distances
|
|
296
|
+
} = info;
|
|
297
|
+
let d0 = ray.direction.dot( v0 );
|
|
298
|
+
let d1 = ray.direction.dot( v1 );
|
|
299
|
+
|
|
300
|
+
if ( d0 > d1 ) {
|
|
301
|
+
|
|
302
|
+
[ d0, d1 ] = [ d1, d0 ];
|
|
303
|
+
|
|
304
|
+
} // return early if the face edge is found to be a subsegment of a line edge meaning the edge will have "hard" normals
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
let found = false;
|
|
308
|
+
|
|
309
|
+
for ( let i = 0, l = distances.length; i < l; i += 2 ) {
|
|
310
|
+
|
|
311
|
+
if ( d0 >= distances[ i ] && d1 <= distances[ i + 1 ] ) {
|
|
312
|
+
|
|
313
|
+
found = true;
|
|
314
|
+
break;
|
|
315
|
+
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
if ( found ) {
|
|
321
|
+
|
|
322
|
+
continue;
|
|
323
|
+
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
}
|
|
329
|
+
|
|
196
330
|
const info = {
|
|
197
331
|
index: index,
|
|
198
332
|
tri: tri
|
|
@@ -433,6 +567,12 @@
|
|
|
433
567
|
|
|
434
568
|
}
|
|
435
569
|
|
|
570
|
+
getVector() {
|
|
571
|
+
|
|
572
|
+
return new THREE.Vector3( parseFloat( this.getToken() ), parseFloat( this.getToken() ), parseFloat( this.getToken() ) );
|
|
573
|
+
|
|
574
|
+
}
|
|
575
|
+
|
|
436
576
|
getRemainingString() {
|
|
437
577
|
|
|
438
578
|
return this.line.substring( this.currentCharIndex, this.lineLength );
|
|
@@ -459,1239 +599,1368 @@
|
|
|
459
599
|
|
|
460
600
|
}
|
|
461
601
|
|
|
462
|
-
class
|
|
602
|
+
class LDrawParsedCache {
|
|
463
603
|
|
|
464
604
|
constructor( loader ) {
|
|
465
605
|
|
|
466
|
-
this.cache = {};
|
|
467
606
|
this.loader = loader;
|
|
607
|
+
this.cache = {};
|
|
468
608
|
|
|
469
609
|
}
|
|
470
610
|
|
|
471
|
-
|
|
611
|
+
cloneResult( original ) {
|
|
472
612
|
|
|
473
|
-
|
|
613
|
+
const result = {}; // vertices are transformed and normals computed before being converted to geometry
|
|
614
|
+
// so these pieces must be cloned.
|
|
474
615
|
|
|
475
|
-
|
|
616
|
+
result.faces = original.faces.map( face => {
|
|
476
617
|
|
|
477
|
-
|
|
618
|
+
return {
|
|
619
|
+
colorCode: face.colorCode,
|
|
620
|
+
material: face.material,
|
|
621
|
+
vertices: face.vertices.map( v => v.clone() ),
|
|
622
|
+
normals: face.normals.map( () => null ),
|
|
623
|
+
faceNormal: null
|
|
624
|
+
};
|
|
478
625
|
|
|
479
|
-
|
|
626
|
+
} );
|
|
627
|
+
result.conditionalSegments = original.conditionalSegments.map( face => {
|
|
480
628
|
|
|
481
|
-
|
|
629
|
+
return {
|
|
630
|
+
colorCode: face.colorCode,
|
|
631
|
+
material: face.material,
|
|
632
|
+
vertices: face.vertices.map( v => v.clone() ),
|
|
633
|
+
controlPoints: face.controlPoints.map( v => v.clone() )
|
|
634
|
+
};
|
|
482
635
|
|
|
483
|
-
|
|
636
|
+
} );
|
|
637
|
+
result.lineSegments = original.lineSegments.map( face => {
|
|
484
638
|
|
|
485
|
-
|
|
639
|
+
return {
|
|
640
|
+
colorCode: face.colorCode,
|
|
641
|
+
material: face.material,
|
|
642
|
+
vertices: face.vertices.map( v => v.clone() )
|
|
643
|
+
};
|
|
486
644
|
|
|
487
|
-
|
|
645
|
+
} ); // none if this is subsequently modified
|
|
488
646
|
|
|
489
|
-
|
|
490
|
-
|
|
647
|
+
result.type = original.type;
|
|
648
|
+
result.category = original.category;
|
|
649
|
+
result.keywords = original.keywords;
|
|
650
|
+
result.subobjects = original.subobjects;
|
|
651
|
+
result.totalFaces = original.totalFaces;
|
|
652
|
+
result.startingConstructionStep = original.startingConstructionStep;
|
|
653
|
+
result.materials = original.materials;
|
|
654
|
+
return result;
|
|
491
655
|
|
|
492
|
-
|
|
656
|
+
}
|
|
493
657
|
|
|
494
|
-
|
|
658
|
+
async fetchData( fileName ) {
|
|
495
659
|
|
|
496
|
-
|
|
660
|
+
let triedLowerCase = false;
|
|
661
|
+
let locationState = FILE_LOCATION_AS_IS;
|
|
497
662
|
|
|
498
|
-
|
|
499
|
-
locationState = locationState + 1;
|
|
500
|
-
break;
|
|
663
|
+
while ( locationState !== FILE_LOCATION_NOT_FOUND ) {
|
|
501
664
|
|
|
502
|
-
|
|
503
|
-
subobjectURL = 'parts/' + subobjectURL;
|
|
504
|
-
locationState = locationState + 1;
|
|
505
|
-
break;
|
|
665
|
+
let subobjectURL = fileName;
|
|
506
666
|
|
|
507
|
-
|
|
508
|
-
subobjectURL = 'p/' + subobjectURL;
|
|
509
|
-
locationState = locationState + 1;
|
|
510
|
-
break;
|
|
667
|
+
switch ( locationState ) {
|
|
511
668
|
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
break;
|
|
669
|
+
case FILE_LOCATION_AS_IS:
|
|
670
|
+
locationState = locationState + 1;
|
|
671
|
+
break;
|
|
516
672
|
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
673
|
+
case FILE_LOCATION_TRY_PARTS:
|
|
674
|
+
subobjectURL = 'parts/' + subobjectURL;
|
|
675
|
+
locationState = locationState + 1;
|
|
676
|
+
break;
|
|
521
677
|
|
|
522
|
-
|
|
523
|
-
|
|
678
|
+
case FILE_LOCATION_TRY_P:
|
|
679
|
+
subobjectURL = 'p/' + subobjectURL;
|
|
680
|
+
locationState = locationState + 1;
|
|
681
|
+
break;
|
|
524
682
|
|
|
525
|
-
|
|
526
|
-
|
|
683
|
+
case FILE_LOCATION_TRY_MODELS:
|
|
684
|
+
subobjectURL = 'models/' + subobjectURL;
|
|
685
|
+
locationState = locationState + 1;
|
|
686
|
+
break;
|
|
527
687
|
|
|
528
|
-
|
|
688
|
+
case FILE_LOCATION_TRY_RELATIVE:
|
|
689
|
+
subobjectURL = fileName.substring( 0, fileName.lastIndexOf( '/' ) + 1 ) + subobjectURL;
|
|
690
|
+
locationState = locationState + 1;
|
|
691
|
+
break;
|
|
529
692
|
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
subobjectURL = fileName;
|
|
533
|
-
triedLowerCase = true;
|
|
534
|
-
locationState = FILE_LOCATION_AS_IS;
|
|
693
|
+
case FILE_LOCATION_TRY_ABSOLUTE:
|
|
694
|
+
if ( triedLowerCase ) {
|
|
535
695
|
|
|
536
|
-
|
|
696
|
+
// Try absolute path
|
|
697
|
+
locationState = FILE_LOCATION_NOT_FOUND;
|
|
537
698
|
|
|
538
|
-
|
|
699
|
+
} else {
|
|
539
700
|
|
|
540
|
-
|
|
701
|
+
// Next attempt is lower case
|
|
702
|
+
fileName = fileName.toLowerCase();
|
|
703
|
+
subobjectURL = fileName;
|
|
704
|
+
triedLowerCase = true;
|
|
705
|
+
locationState = FILE_LOCATION_AS_IS;
|
|
706
|
+
|
|
707
|
+
}
|
|
541
708
|
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
fileLoader.setRequestHeader( loader.requestHeader );
|
|
546
|
-
fileLoader.setWithCredentials( loader.withCredentials );
|
|
709
|
+
break;
|
|
710
|
+
|
|
711
|
+
}
|
|
547
712
|
|
|
548
|
-
|
|
713
|
+
const loader = this.loader;
|
|
714
|
+
const fileLoader = new THREE.FileLoader( loader.manager );
|
|
715
|
+
fileLoader.setPath( loader.partsLibraryPath );
|
|
716
|
+
fileLoader.setRequestHeader( loader.requestHeader );
|
|
717
|
+
fileLoader.setWithCredentials( loader.withCredentials );
|
|
549
718
|
|
|
550
|
-
|
|
551
|
-
this.setData( fileName, text );
|
|
552
|
-
resolve( text );
|
|
553
|
-
return;
|
|
719
|
+
try {
|
|
554
720
|
|
|
555
|
-
|
|
721
|
+
const text = await fileLoader.loadAsync( subobjectURL );
|
|
722
|
+
return text;
|
|
556
723
|
|
|
557
|
-
|
|
724
|
+
} catch {
|
|
558
725
|
|
|
559
|
-
|
|
726
|
+
continue;
|
|
560
727
|
|
|
561
728
|
}
|
|
562
729
|
|
|
563
|
-
|
|
730
|
+
}
|
|
564
731
|
|
|
565
|
-
|
|
566
|
-
return this.cache[ fileName ];
|
|
732
|
+
throw new Error( 'LDrawLoader: Subobject "' + fileName + '" could not be loaded.' );
|
|
567
733
|
|
|
568
734
|
}
|
|
569
735
|
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
function sortByMaterial( a, b ) {
|
|
736
|
+
parse( text ) {
|
|
573
737
|
|
|
574
|
-
|
|
738
|
+
const loader = this.loader; // final results
|
|
575
739
|
|
|
576
|
-
|
|
740
|
+
const faces = [];
|
|
741
|
+
const lineSegments = [];
|
|
742
|
+
const conditionalSegments = [];
|
|
743
|
+
const subobjects = [];
|
|
744
|
+
const materials = {};
|
|
577
745
|
|
|
578
|
-
|
|
746
|
+
const getLocalMaterial = colorCode => {
|
|
579
747
|
|
|
580
|
-
|
|
748
|
+
return colorCode in materials ? materials[ colorCode ] : null;
|
|
581
749
|
|
|
582
|
-
|
|
750
|
+
};
|
|
583
751
|
|
|
584
|
-
|
|
752
|
+
let type = 'Model';
|
|
753
|
+
let category = null;
|
|
754
|
+
let keywords = null;
|
|
755
|
+
let totalFaces = 0; // split into lines
|
|
585
756
|
|
|
586
|
-
|
|
757
|
+
if ( text.indexOf( '\r\n' ) !== - 1 ) {
|
|
587
758
|
|
|
588
|
-
|
|
759
|
+
// This is faster than String.split with regex that splits on both
|
|
760
|
+
text = text.replace( /\r\n/g, '\n' );
|
|
589
761
|
|
|
590
|
-
|
|
762
|
+
}
|
|
591
763
|
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
764
|
+
const lines = text.split( '\n' );
|
|
765
|
+
const numLines = lines.length;
|
|
766
|
+
let parsingEmbeddedFiles = false;
|
|
767
|
+
let currentEmbeddedFileName = null;
|
|
768
|
+
let currentEmbeddedText = null;
|
|
769
|
+
let bfcCertified = false;
|
|
770
|
+
let bfcCCW = true;
|
|
771
|
+
let bfcInverted = false;
|
|
772
|
+
let bfcCull = true;
|
|
773
|
+
let startingConstructionStep = false; // Parse all line commands
|
|
596
774
|
|
|
597
|
-
|
|
775
|
+
for ( let lineIndex = 0; lineIndex < numLines; lineIndex ++ ) {
|
|
598
776
|
|
|
599
|
-
|
|
777
|
+
const line = lines[ lineIndex ];
|
|
778
|
+
if ( line.length === 0 ) continue;
|
|
600
779
|
|
|
601
|
-
|
|
780
|
+
if ( parsingEmbeddedFiles ) {
|
|
602
781
|
|
|
603
|
-
|
|
604
|
-
const normals = elementSize === 3 ? new Float32Array( elementSize * totalElements * 3 ) : null;
|
|
605
|
-
const materials = [];
|
|
606
|
-
const quadArray = new Array( 6 );
|
|
607
|
-
const bufferGeometry = new THREE.BufferGeometry();
|
|
608
|
-
let prevMaterial = null;
|
|
609
|
-
let index0 = 0;
|
|
610
|
-
let numGroupVerts = 0;
|
|
611
|
-
let offset = 0;
|
|
782
|
+
if ( line.startsWith( '0 FILE ' ) ) {
|
|
612
783
|
|
|
613
|
-
|
|
784
|
+
// Save previous embedded file in the cache
|
|
785
|
+
this.setData( currentEmbeddedFileName, currentEmbeddedText ); // New embedded text file
|
|
614
786
|
|
|
615
|
-
|
|
616
|
-
|
|
787
|
+
currentEmbeddedFileName = line.substring( 7 );
|
|
788
|
+
currentEmbeddedText = '';
|
|
617
789
|
|
|
618
|
-
|
|
790
|
+
} else {
|
|
619
791
|
|
|
620
|
-
|
|
621
|
-
quadArray[ 1 ] = vertices[ 1 ];
|
|
622
|
-
quadArray[ 2 ] = vertices[ 2 ];
|
|
623
|
-
quadArray[ 3 ] = vertices[ 0 ];
|
|
624
|
-
quadArray[ 4 ] = vertices[ 2 ];
|
|
625
|
-
quadArray[ 5 ] = vertices[ 3 ];
|
|
626
|
-
vertices = quadArray;
|
|
792
|
+
currentEmbeddedText += line + '\n';
|
|
627
793
|
|
|
628
|
-
|
|
794
|
+
}
|
|
629
795
|
|
|
630
|
-
|
|
796
|
+
continue;
|
|
631
797
|
|
|
632
|
-
|
|
633
|
-
const index = offset + j * 3;
|
|
634
|
-
positions[ index + 0 ] = v.x;
|
|
635
|
-
positions[ index + 1 ] = v.y;
|
|
636
|
-
positions[ index + 2 ] = v.z;
|
|
798
|
+
}
|
|
637
799
|
|
|
638
|
-
|
|
800
|
+
const lp = new LineParser( line, lineIndex + 1 );
|
|
801
|
+
lp.seekNonSpace();
|
|
639
802
|
|
|
640
|
-
|
|
803
|
+
if ( lp.isAtTheEnd() ) {
|
|
641
804
|
|
|
642
|
-
|
|
805
|
+
// Empty line
|
|
806
|
+
continue;
|
|
643
807
|
|
|
644
|
-
|
|
808
|
+
} // Parse the line type
|
|
645
809
|
|
|
646
|
-
quadArray[ 0 ] = elemNormals[ 0 ];
|
|
647
|
-
quadArray[ 1 ] = elemNormals[ 1 ];
|
|
648
|
-
quadArray[ 2 ] = elemNormals[ 2 ];
|
|
649
|
-
quadArray[ 3 ] = elemNormals[ 0 ];
|
|
650
|
-
quadArray[ 4 ] = elemNormals[ 2 ];
|
|
651
|
-
quadArray[ 5 ] = elemNormals[ 3 ];
|
|
652
|
-
elemNormals = quadArray;
|
|
653
810
|
|
|
654
|
-
|
|
811
|
+
const lineType = lp.getToken();
|
|
812
|
+
let material;
|
|
813
|
+
let colorCode;
|
|
814
|
+
let segment;
|
|
815
|
+
let ccw;
|
|
816
|
+
let doubleSided;
|
|
817
|
+
let v0, v1, v2, v3, c0, c1;
|
|
655
818
|
|
|
656
|
-
|
|
819
|
+
switch ( lineType ) {
|
|
657
820
|
|
|
658
|
-
|
|
821
|
+
// Line type 0: Comment or META
|
|
822
|
+
case '0':
|
|
823
|
+
// Parse meta directive
|
|
824
|
+
const meta = lp.getToken();
|
|
659
825
|
|
|
660
|
-
|
|
826
|
+
if ( meta ) {
|
|
661
827
|
|
|
662
|
-
|
|
828
|
+
switch ( meta ) {
|
|
663
829
|
|
|
664
|
-
|
|
830
|
+
case '!LDRAW_ORG':
|
|
831
|
+
type = lp.getToken();
|
|
832
|
+
break;
|
|
665
833
|
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
normals[ index + 1 ] = n.y;
|
|
669
|
-
normals[ index + 2 ] = n.z;
|
|
834
|
+
case '!COLOUR':
|
|
835
|
+
material = loader.parseColorMetaDirective( lp );
|
|
670
836
|
|
|
671
|
-
|
|
837
|
+
if ( material ) {
|
|
672
838
|
|
|
673
|
-
|
|
839
|
+
materials[ material.userData.code ] = material;
|
|
674
840
|
|
|
675
|
-
|
|
841
|
+
} else {
|
|
676
842
|
|
|
677
|
-
|
|
843
|
+
console.warn( 'LDrawLoader: Error parsing material' + lp.getLineNumberString() );
|
|
678
844
|
|
|
679
|
-
|
|
845
|
+
}
|
|
680
846
|
|
|
681
|
-
|
|
847
|
+
break;
|
|
682
848
|
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
numGroupVerts = vertices.length;
|
|
849
|
+
case '!CATEGORY':
|
|
850
|
+
category = lp.getToken();
|
|
851
|
+
break;
|
|
687
852
|
|
|
688
|
-
|
|
853
|
+
case '!KEYWORDS':
|
|
854
|
+
const newKeywords = lp.getRemainingString().split( ',' );
|
|
689
855
|
|
|
690
|
-
|
|
856
|
+
if ( newKeywords.length > 0 ) {
|
|
691
857
|
|
|
692
|
-
|
|
858
|
+
if ( ! keywords ) {
|
|
693
859
|
|
|
694
|
-
|
|
860
|
+
keywords = [];
|
|
695
861
|
|
|
696
|
-
|
|
862
|
+
}
|
|
697
863
|
|
|
698
|
-
|
|
864
|
+
newKeywords.forEach( function ( keyword ) {
|
|
699
865
|
|
|
700
|
-
|
|
866
|
+
keywords.push( keyword.trim() );
|
|
701
867
|
|
|
702
|
-
|
|
868
|
+
} );
|
|
703
869
|
|
|
704
|
-
|
|
870
|
+
}
|
|
705
871
|
|
|
706
|
-
|
|
872
|
+
break;
|
|
707
873
|
|
|
708
|
-
|
|
874
|
+
case 'FILE':
|
|
875
|
+
if ( lineIndex > 0 ) {
|
|
709
876
|
|
|
710
|
-
|
|
877
|
+
// Start embedded text files parsing
|
|
878
|
+
parsingEmbeddedFiles = true;
|
|
879
|
+
currentEmbeddedFileName = lp.getRemainingString();
|
|
880
|
+
currentEmbeddedText = '';
|
|
881
|
+
bfcCertified = false;
|
|
882
|
+
bfcCCW = true;
|
|
711
883
|
|
|
712
|
-
|
|
884
|
+
}
|
|
713
885
|
|
|
714
|
-
|
|
886
|
+
break;
|
|
715
887
|
|
|
716
|
-
|
|
888
|
+
case 'BFC':
|
|
889
|
+
// Changes to the backface culling state
|
|
890
|
+
while ( ! lp.isAtTheEnd() ) {
|
|
717
891
|
|
|
718
|
-
|
|
892
|
+
const token = lp.getToken();
|
|
719
893
|
|
|
720
|
-
|
|
894
|
+
switch ( token ) {
|
|
721
895
|
|
|
722
|
-
|
|
896
|
+
case 'CERTIFY':
|
|
897
|
+
case 'NOCERTIFY':
|
|
898
|
+
bfcCertified = token === 'CERTIFY';
|
|
899
|
+
bfcCCW = true;
|
|
900
|
+
break;
|
|
723
901
|
|
|
724
|
-
|
|
902
|
+
case 'CW':
|
|
903
|
+
case 'CCW':
|
|
904
|
+
bfcCCW = token === 'CCW';
|
|
905
|
+
break;
|
|
725
906
|
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
const directionArray = new Float32Array( elements.length * 3 * 2 );
|
|
907
|
+
case 'INVERTNEXT':
|
|
908
|
+
bfcInverted = true;
|
|
909
|
+
break;
|
|
730
910
|
|
|
731
|
-
|
|
911
|
+
case 'CLIP':
|
|
912
|
+
case 'NOCLIP':
|
|
913
|
+
bfcCull = token === 'CLIP';
|
|
914
|
+
break;
|
|
732
915
|
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
const c0 = controlPoints[ 0 ];
|
|
737
|
-
const c1 = controlPoints[ 1 ];
|
|
738
|
-
const v0 = vertices[ 0 ];
|
|
739
|
-
const v1 = vertices[ 1 ];
|
|
740
|
-
const index = i * 3 * 2;
|
|
741
|
-
controlArray0[ index + 0 ] = c0.x;
|
|
742
|
-
controlArray0[ index + 1 ] = c0.y;
|
|
743
|
-
controlArray0[ index + 2 ] = c0.z;
|
|
744
|
-
controlArray0[ index + 3 ] = c0.x;
|
|
745
|
-
controlArray0[ index + 4 ] = c0.y;
|
|
746
|
-
controlArray0[ index + 5 ] = c0.z;
|
|
747
|
-
controlArray1[ index + 0 ] = c1.x;
|
|
748
|
-
controlArray1[ index + 1 ] = c1.y;
|
|
749
|
-
controlArray1[ index + 2 ] = c1.z;
|
|
750
|
-
controlArray1[ index + 3 ] = c1.x;
|
|
751
|
-
controlArray1[ index + 4 ] = c1.y;
|
|
752
|
-
controlArray1[ index + 5 ] = c1.z;
|
|
753
|
-
directionArray[ index + 0 ] = v1.x - v0.x;
|
|
754
|
-
directionArray[ index + 1 ] = v1.y - v0.y;
|
|
755
|
-
directionArray[ index + 2 ] = v1.z - v0.z;
|
|
756
|
-
directionArray[ index + 3 ] = v1.x - v0.x;
|
|
757
|
-
directionArray[ index + 4 ] = v1.y - v0.y;
|
|
758
|
-
directionArray[ index + 5 ] = v1.z - v0.z;
|
|
916
|
+
default:
|
|
917
|
+
console.warn( 'THREE.LDrawLoader: BFC directive "' + token + '" is unknown.' );
|
|
918
|
+
break;
|
|
759
919
|
|
|
760
|
-
|
|
920
|
+
}
|
|
761
921
|
|
|
762
|
-
|
|
763
|
-
bufferGeometry.setAttribute( 'control1', new THREE.BufferAttribute( controlArray1, 3, false ) );
|
|
764
|
-
bufferGeometry.setAttribute( 'direction', new THREE.BufferAttribute( directionArray, 3, false ) );
|
|
922
|
+
}
|
|
765
923
|
|
|
766
|
-
|
|
924
|
+
break;
|
|
767
925
|
|
|
768
|
-
|
|
926
|
+
case 'STEP':
|
|
927
|
+
startingConstructionStep = true;
|
|
928
|
+
break;
|
|
769
929
|
|
|
770
|
-
|
|
930
|
+
default:
|
|
931
|
+
// Other meta directives are not implemented
|
|
932
|
+
break;
|
|
771
933
|
|
|
934
|
+
}
|
|
772
935
|
|
|
773
|
-
|
|
936
|
+
}
|
|
774
937
|
|
|
775
|
-
|
|
938
|
+
break;
|
|
939
|
+
// Line type 1: Sub-object file
|
|
776
940
|
|
|
777
|
-
|
|
941
|
+
case '1':
|
|
942
|
+
colorCode = lp.getToken();
|
|
943
|
+
material = getLocalMaterial( colorCode );
|
|
944
|
+
const posX = parseFloat( lp.getToken() );
|
|
945
|
+
const posY = parseFloat( lp.getToken() );
|
|
946
|
+
const posZ = parseFloat( lp.getToken() );
|
|
947
|
+
const m0 = parseFloat( lp.getToken() );
|
|
948
|
+
const m1 = parseFloat( lp.getToken() );
|
|
949
|
+
const m2 = parseFloat( lp.getToken() );
|
|
950
|
+
const m3 = parseFloat( lp.getToken() );
|
|
951
|
+
const m4 = parseFloat( lp.getToken() );
|
|
952
|
+
const m5 = parseFloat( lp.getToken() );
|
|
953
|
+
const m6 = parseFloat( lp.getToken() );
|
|
954
|
+
const m7 = parseFloat( lp.getToken() );
|
|
955
|
+
const m8 = parseFloat( lp.getToken() );
|
|
956
|
+
const matrix = new THREE.Matrix4().set( m0, m1, m2, posX, m3, m4, m5, posY, m6, m7, m8, posZ, 0, 0, 0, 1 );
|
|
957
|
+
let fileName = lp.getRemainingString().trim().replace( /\\/g, '/' );
|
|
778
958
|
|
|
779
|
-
|
|
780
|
-
// This also allows to handle the embedded text files ("0 FILE" lines)
|
|
959
|
+
if ( loader.fileMap[ fileName ] ) {
|
|
781
960
|
|
|
782
|
-
|
|
961
|
+
// Found the subobject path in the preloaded file path map
|
|
962
|
+
fileName = loader.fileMap[ fileName ];
|
|
783
963
|
|
|
784
|
-
|
|
785
|
-
this.rootParseScope = this.newParseScopeLevel(); // Add default main triangle and line edge materials (used in pieces that can be coloured with a main color)
|
|
964
|
+
} else {
|
|
786
965
|
|
|
787
|
-
|
|
788
|
-
|
|
966
|
+
// Standardized subfolders
|
|
967
|
+
if ( fileName.startsWith( 's/' ) ) {
|
|
789
968
|
|
|
790
|
-
|
|
969
|
+
fileName = 'parts/' + fileName;
|
|
791
970
|
|
|
792
|
-
|
|
971
|
+
} else if ( fileName.startsWith( '48/' ) ) {
|
|
793
972
|
|
|
794
|
-
|
|
973
|
+
fileName = 'p/' + fileName;
|
|
795
974
|
|
|
796
|
-
|
|
975
|
+
}
|
|
797
976
|
|
|
798
|
-
|
|
977
|
+
}
|
|
799
978
|
|
|
800
|
-
|
|
801
|
-
|
|
979
|
+
subobjects.push( {
|
|
980
|
+
material: material,
|
|
981
|
+
colorCode: colorCode,
|
|
982
|
+
matrix: matrix,
|
|
983
|
+
fileName: fileName,
|
|
984
|
+
inverted: bfcInverted,
|
|
985
|
+
startingConstructionStep: startingConstructionStep
|
|
986
|
+
} );
|
|
987
|
+
bfcInverted = false;
|
|
988
|
+
break;
|
|
989
|
+
// Line type 2: Line segment
|
|
802
990
|
|
|
803
|
-
|
|
991
|
+
case '2':
|
|
992
|
+
colorCode = lp.getToken();
|
|
993
|
+
material = getLocalMaterial( colorCode );
|
|
994
|
+
v0 = lp.getVector();
|
|
995
|
+
v1 = lp.getVector();
|
|
996
|
+
segment = {
|
|
997
|
+
material: material,
|
|
998
|
+
colorCode: colorCode,
|
|
999
|
+
vertices: [ v0, v1 ]
|
|
1000
|
+
};
|
|
1001
|
+
lineSegments.push( segment );
|
|
1002
|
+
break;
|
|
1003
|
+
// Line type 5: Conditional Line segment
|
|
804
1004
|
|
|
805
|
-
|
|
1005
|
+
case '5':
|
|
1006
|
+
colorCode = lp.getToken();
|
|
1007
|
+
material = getLocalMaterial( colorCode );
|
|
1008
|
+
v0 = lp.getVector();
|
|
1009
|
+
v1 = lp.getVector();
|
|
1010
|
+
c0 = lp.getVector();
|
|
1011
|
+
c1 = lp.getVector();
|
|
1012
|
+
segment = {
|
|
1013
|
+
material: material,
|
|
1014
|
+
colorCode: colorCode,
|
|
1015
|
+
vertices: [ v0, v1 ],
|
|
1016
|
+
controlPoints: [ c0, c1 ]
|
|
1017
|
+
};
|
|
1018
|
+
conditionalSegments.push( segment );
|
|
1019
|
+
break;
|
|
1020
|
+
// Line type 3: Triangle
|
|
806
1021
|
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
const colorLineRegex = /^0 !COLOUR/;
|
|
813
|
-
const lines = text.split( /[\n\r]/g );
|
|
814
|
-
const materials = [];
|
|
1022
|
+
case '3':
|
|
1023
|
+
colorCode = lp.getToken();
|
|
1024
|
+
material = getLocalMaterial( colorCode );
|
|
1025
|
+
ccw = bfcCCW;
|
|
1026
|
+
doubleSided = ! bfcCertified || ! bfcCull;
|
|
815
1027
|
|
|
816
|
-
|
|
1028
|
+
if ( ccw === true ) {
|
|
817
1029
|
|
|
818
|
-
|
|
1030
|
+
v0 = lp.getVector();
|
|
1031
|
+
v1 = lp.getVector();
|
|
1032
|
+
v2 = lp.getVector();
|
|
819
1033
|
|
|
820
|
-
|
|
1034
|
+
} else {
|
|
821
1035
|
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
1036
|
+
v2 = lp.getVector();
|
|
1037
|
+
v1 = lp.getVector();
|
|
1038
|
+
v0 = lp.getVector();
|
|
1039
|
+
|
|
1040
|
+
}
|
|
1041
|
+
|
|
1042
|
+
faces.push( {
|
|
1043
|
+
material: material,
|
|
1044
|
+
colorCode: colorCode,
|
|
1045
|
+
faceNormal: null,
|
|
1046
|
+
vertices: [ v0, v1, v2 ],
|
|
1047
|
+
normals: [ null, null, null ]
|
|
1048
|
+
} );
|
|
1049
|
+
totalFaces ++;
|
|
1050
|
+
|
|
1051
|
+
if ( doubleSided === true ) {
|
|
1052
|
+
|
|
1053
|
+
faces.push( {
|
|
1054
|
+
material: material,
|
|
1055
|
+
colorCode: colorCode,
|
|
1056
|
+
faceNormal: null,
|
|
1057
|
+
vertices: [ v2, v1, v0 ],
|
|
1058
|
+
normals: [ null, null, null ]
|
|
1059
|
+
} );
|
|
1060
|
+
totalFaces ++;
|
|
1061
|
+
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
break;
|
|
1065
|
+
// Line type 4: Quadrilateral
|
|
1066
|
+
|
|
1067
|
+
case '4':
|
|
1068
|
+
colorCode = lp.getToken();
|
|
1069
|
+
material = getLocalMaterial( colorCode );
|
|
1070
|
+
ccw = bfcCCW;
|
|
1071
|
+
doubleSided = ! bfcCertified || ! bfcCull;
|
|
1072
|
+
|
|
1073
|
+
if ( ccw === true ) {
|
|
1074
|
+
|
|
1075
|
+
v0 = lp.getVector();
|
|
1076
|
+
v1 = lp.getVector();
|
|
1077
|
+
v2 = lp.getVector();
|
|
1078
|
+
v3 = lp.getVector();
|
|
1079
|
+
|
|
1080
|
+
} else {
|
|
1081
|
+
|
|
1082
|
+
v3 = lp.getVector();
|
|
1083
|
+
v2 = lp.getVector();
|
|
1084
|
+
v1 = lp.getVector();
|
|
1085
|
+
v0 = lp.getVector();
|
|
1086
|
+
|
|
1087
|
+
} // specifically place the triangle diagonal in the v0 and v1 slots so we can
|
|
1088
|
+
// account for the doubling of vertices later when smoothing normals.
|
|
1089
|
+
|
|
1090
|
+
|
|
1091
|
+
faces.push( {
|
|
1092
|
+
material: material,
|
|
1093
|
+
colorCode: colorCode,
|
|
1094
|
+
faceNormal: null,
|
|
1095
|
+
vertices: [ v0, v1, v2, v3 ],
|
|
1096
|
+
normals: [ null, null, null, null ]
|
|
1097
|
+
} );
|
|
1098
|
+
totalFaces += 2;
|
|
1099
|
+
|
|
1100
|
+
if ( doubleSided === true ) {
|
|
1101
|
+
|
|
1102
|
+
faces.push( {
|
|
1103
|
+
material: material,
|
|
1104
|
+
colorCode: colorCode,
|
|
1105
|
+
faceNormal: null,
|
|
1106
|
+
vertices: [ v3, v2, v1, v0 ],
|
|
1107
|
+
normals: [ null, null, null, null ]
|
|
1108
|
+
} );
|
|
1109
|
+
totalFaces += 2;
|
|
1110
|
+
|
|
1111
|
+
}
|
|
1112
|
+
|
|
1113
|
+
break;
|
|
1114
|
+
|
|
1115
|
+
default:
|
|
1116
|
+
throw new Error( 'LDrawLoader: Unknown line type "' + lineType + '"' + lp.getLineNumberString() + '.' );
|
|
825
1117
|
|
|
826
1118
|
}
|
|
827
1119
|
|
|
828
1120
|
}
|
|
829
1121
|
|
|
830
|
-
|
|
1122
|
+
if ( parsingEmbeddedFiles ) {
|
|
1123
|
+
|
|
1124
|
+
this.setData( currentEmbeddedFileName, currentEmbeddedText );
|
|
1125
|
+
|
|
1126
|
+
}
|
|
1127
|
+
|
|
1128
|
+
return {
|
|
1129
|
+
faces,
|
|
1130
|
+
conditionalSegments,
|
|
1131
|
+
lineSegments,
|
|
1132
|
+
type,
|
|
1133
|
+
category,
|
|
1134
|
+
keywords,
|
|
1135
|
+
subobjects,
|
|
1136
|
+
totalFaces,
|
|
1137
|
+
startingConstructionStep,
|
|
1138
|
+
materials
|
|
1139
|
+
};
|
|
831
1140
|
|
|
832
1141
|
}
|
|
833
1142
|
|
|
834
|
-
|
|
1143
|
+
loadData( fileName ) {
|
|
1144
|
+
|
|
1145
|
+
const key = fileName.toLowerCase();
|
|
835
1146
|
|
|
836
|
-
if ( ! this.
|
|
1147
|
+
if ( ! ( key in this.cache ) ) {
|
|
837
1148
|
|
|
838
|
-
this.
|
|
1149
|
+
this.cache[ key ] = this.fetchData( fileName ).then( text => {
|
|
1150
|
+
|
|
1151
|
+
return this.parse( text );
|
|
1152
|
+
|
|
1153
|
+
} );
|
|
839
1154
|
|
|
840
1155
|
}
|
|
841
1156
|
|
|
842
|
-
|
|
843
|
-
fileLoader.setPath( this.path );
|
|
844
|
-
fileLoader.setRequestHeader( this.requestHeader );
|
|
845
|
-
fileLoader.setWithCredentials( this.withCredentials );
|
|
846
|
-
fileLoader.load( url, text => {
|
|
1157
|
+
return this.cache[ key ].then( result => {
|
|
847
1158
|
|
|
848
|
-
|
|
1159
|
+
return this.cloneResult( result );
|
|
849
1160
|
|
|
850
|
-
|
|
1161
|
+
} );
|
|
851
1162
|
|
|
852
|
-
|
|
1163
|
+
}
|
|
853
1164
|
|
|
854
|
-
|
|
1165
|
+
setData( fileName, text ) {
|
|
1166
|
+
|
|
1167
|
+
const key = fileName.toLowerCase();
|
|
1168
|
+
this.cache[ key ] = Promise.resolve( this.parse( text ) );
|
|
855
1169
|
|
|
856
1170
|
}
|
|
857
1171
|
|
|
858
|
-
|
|
1172
|
+
}
|
|
859
1173
|
|
|
860
|
-
|
|
861
|
-
this.processObject( text, null, path, this.rootParseScope ).then( function ( result ) {
|
|
1174
|
+
function sortByMaterial( a, b ) {
|
|
862
1175
|
|
|
863
|
-
|
|
1176
|
+
if ( a.colorCode === b.colorCode ) {
|
|
864
1177
|
|
|
865
|
-
|
|
1178
|
+
return 0;
|
|
866
1179
|
|
|
867
1180
|
}
|
|
868
1181
|
|
|
869
|
-
|
|
1182
|
+
if ( a.colorCode < b.colorCode ) {
|
|
870
1183
|
|
|
871
|
-
|
|
872
|
-
this.rootParseScope = this.newParseScopeLevel( materials );
|
|
873
|
-
this.rootParseScope.isFromParse = false;
|
|
874
|
-
this.materials = materials;
|
|
875
|
-
return this;
|
|
1184
|
+
return - 1;
|
|
876
1185
|
|
|
877
1186
|
}
|
|
878
1187
|
|
|
879
|
-
|
|
1188
|
+
return 1;
|
|
880
1189
|
|
|
881
|
-
|
|
882
|
-
return this;
|
|
1190
|
+
}
|
|
883
1191
|
|
|
884
|
-
|
|
1192
|
+
function createObject( elements, elementSize, isConditionalSegments = false, totalElements = null ) {
|
|
885
1193
|
|
|
886
|
-
|
|
1194
|
+
// Creates a THREE.LineSegments (elementSize = 2) or a THREE.Mesh (elementSize = 3 )
|
|
1195
|
+
// With per face / segment material, implemented with mesh groups and materials array
|
|
1196
|
+
// Sort the faces or line segments by color code to make later the mesh groups
|
|
1197
|
+
elements.sort( sortByMaterial );
|
|
887
1198
|
|
|
888
|
-
|
|
889
|
-
const matLib = {};
|
|
1199
|
+
if ( totalElements === null ) {
|
|
890
1200
|
|
|
891
|
-
|
|
1201
|
+
totalElements = elements.length;
|
|
892
1202
|
|
|
893
|
-
|
|
1203
|
+
}
|
|
894
1204
|
|
|
895
|
-
|
|
896
|
-
|
|
1205
|
+
const positions = new Float32Array( elementSize * totalElements * 3 );
|
|
1206
|
+
const normals = elementSize === 3 ? new Float32Array( elementSize * totalElements * 3 ) : null;
|
|
1207
|
+
const materials = [];
|
|
1208
|
+
const quadArray = new Array( 6 );
|
|
1209
|
+
const bufferGeometry = new THREE.BufferGeometry();
|
|
1210
|
+
let prevMaterial = null;
|
|
1211
|
+
let index0 = 0;
|
|
1212
|
+
let numGroupVerts = 0;
|
|
1213
|
+
let offset = 0;
|
|
897
1214
|
|
|
898
|
-
|
|
1215
|
+
for ( let iElem = 0, nElem = elements.length; iElem < nElem; iElem ++ ) {
|
|
899
1216
|
|
|
900
|
-
|
|
1217
|
+
const elem = elements[ iElem ];
|
|
1218
|
+
let vertices = elem.vertices;
|
|
901
1219
|
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
category: null,
|
|
912
|
-
keywords: null,
|
|
913
|
-
// Current subobject
|
|
914
|
-
currentFileName: null,
|
|
915
|
-
mainColourCode: parentScope ? parentScope.mainColourCode : '16',
|
|
916
|
-
mainEdgeColourCode: parentScope ? parentScope.mainEdgeColourCode : '24',
|
|
917
|
-
currentMatrix: new THREE.Matrix4(),
|
|
918
|
-
matrix: new THREE.Matrix4(),
|
|
919
|
-
type: 'Model',
|
|
920
|
-
groupObject: null,
|
|
921
|
-
// If false, it is a root material scope previous to parse
|
|
922
|
-
isFromParse: true,
|
|
923
|
-
faces: [],
|
|
924
|
-
lineSegments: [],
|
|
925
|
-
conditionalSegments: [],
|
|
926
|
-
totalFaces: 0,
|
|
927
|
-
// If true, this object is the start of a construction step
|
|
928
|
-
startingConstructionStep: false
|
|
929
|
-
};
|
|
930
|
-
return newParseScope;
|
|
1220
|
+
if ( vertices.length === 4 ) {
|
|
1221
|
+
|
|
1222
|
+
quadArray[ 0 ] = vertices[ 0 ];
|
|
1223
|
+
quadArray[ 1 ] = vertices[ 1 ];
|
|
1224
|
+
quadArray[ 2 ] = vertices[ 2 ];
|
|
1225
|
+
quadArray[ 3 ] = vertices[ 0 ];
|
|
1226
|
+
quadArray[ 4 ] = vertices[ 2 ];
|
|
1227
|
+
quadArray[ 5 ] = vertices[ 3 ];
|
|
1228
|
+
vertices = quadArray;
|
|
931
1229
|
|
|
932
|
-
|
|
1230
|
+
}
|
|
933
1231
|
|
|
934
|
-
|
|
1232
|
+
for ( let j = 0, l = vertices.length; j < l; j ++ ) {
|
|
935
1233
|
|
|
936
|
-
|
|
937
|
-
|
|
1234
|
+
const v = vertices[ j ];
|
|
1235
|
+
const index = offset + j * 3;
|
|
1236
|
+
positions[ index + 0 ] = v.x;
|
|
1237
|
+
positions[ index + 1 ] = v.y;
|
|
1238
|
+
positions[ index + 2 ] = v.z;
|
|
938
1239
|
|
|
939
|
-
if
|
|
1240
|
+
} // create the normals array if this is a set of faces
|
|
940
1241
|
|
|
941
|
-
this.materials.push( material );
|
|
942
1242
|
|
|
943
|
-
|
|
1243
|
+
if ( elementSize === 3 ) {
|
|
944
1244
|
|
|
945
|
-
|
|
946
|
-
return this;
|
|
1245
|
+
if ( ! elem.faceNormal ) {
|
|
947
1246
|
|
|
948
|
-
|
|
1247
|
+
const v0 = vertices[ 0 ];
|
|
1248
|
+
const v1 = vertices[ 1 ];
|
|
1249
|
+
const v2 = vertices[ 2 ];
|
|
949
1250
|
|
|
950
|
-
|
|
1251
|
+
_tempVec0.subVectors( v1, v0 );
|
|
951
1252
|
|
|
952
|
-
|
|
953
|
-
if ( colourCode.startsWith( '0x2' ) ) {
|
|
1253
|
+
_tempVec1.subVectors( v2, v1 );
|
|
954
1254
|
|
|
955
|
-
|
|
956
|
-
const colour = colourCode.substring( 3 );
|
|
957
|
-
return this.parseColourMetaDirective( new LineParser( 'Direct_Color_' + colour + ' CODE -1 VALUE #' + colour + ' EDGE #' + colour + '' ) );
|
|
1255
|
+
elem.faceNormal = new THREE.Vector3().crossVectors( _tempVec0, _tempVec1 ).normalize();
|
|
958
1256
|
|
|
959
|
-
|
|
1257
|
+
}
|
|
960
1258
|
|
|
961
|
-
|
|
1259
|
+
let elemNormals = elem.normals;
|
|
962
1260
|
|
|
963
|
-
|
|
1261
|
+
if ( elemNormals.length === 4 ) {
|
|
964
1262
|
|
|
965
|
-
|
|
1263
|
+
quadArray[ 0 ] = elemNormals[ 0 ];
|
|
1264
|
+
quadArray[ 1 ] = elemNormals[ 1 ];
|
|
1265
|
+
quadArray[ 2 ] = elemNormals[ 2 ];
|
|
1266
|
+
quadArray[ 3 ] = elemNormals[ 0 ];
|
|
1267
|
+
quadArray[ 4 ] = elemNormals[ 2 ];
|
|
1268
|
+
quadArray[ 5 ] = elemNormals[ 3 ];
|
|
1269
|
+
elemNormals = quadArray;
|
|
966
1270
|
|
|
967
|
-
|
|
1271
|
+
}
|
|
968
1272
|
|
|
969
|
-
|
|
1273
|
+
for ( let j = 0, l = elemNormals.length; j < l; j ++ ) {
|
|
970
1274
|
|
|
971
|
-
|
|
1275
|
+
// use face normal if a vertex normal is not provided
|
|
1276
|
+
let n = elem.faceNormal;
|
|
972
1277
|
|
|
973
|
-
|
|
1278
|
+
if ( elemNormals[ j ] ) {
|
|
974
1279
|
|
|
975
|
-
|
|
1280
|
+
n = elemNormals[ j ].norm;
|
|
976
1281
|
|
|
1282
|
+
}
|
|
977
1283
|
|
|
978
|
-
|
|
1284
|
+
const index = offset + j * 3;
|
|
1285
|
+
normals[ index + 0 ] = n.x;
|
|
1286
|
+
normals[ index + 1 ] = n.y;
|
|
1287
|
+
normals[ index + 2 ] = n.z;
|
|
979
1288
|
|
|
980
|
-
|
|
1289
|
+
}
|
|
981
1290
|
|
|
982
|
-
|
|
1291
|
+
}
|
|
983
1292
|
|
|
984
|
-
|
|
985
|
-
let code = null; // Triangle and line colours
|
|
1293
|
+
if ( prevMaterial !== elem.material ) {
|
|
986
1294
|
|
|
987
|
-
|
|
988
|
-
let edgeColour = 0xFF00FF; // Transparency
|
|
1295
|
+
if ( prevMaterial !== null ) {
|
|
989
1296
|
|
|
990
|
-
|
|
991
|
-
let isTransparent = false; // Self-illumination:
|
|
1297
|
+
bufferGeometry.addGroup( index0, numGroupVerts, materials.length - 1 );
|
|
992
1298
|
|
|
993
|
-
|
|
994
|
-
let finishType = FINISH_TYPE_DEFAULT;
|
|
995
|
-
let edgeMaterial = null;
|
|
996
|
-
const name = lineParser.getToken();
|
|
1299
|
+
}
|
|
997
1300
|
|
|
998
|
-
|
|
1301
|
+
const material = elem.material;
|
|
999
1302
|
|
|
1000
|
-
|
|
1303
|
+
if ( elementSize === 3 ) {
|
|
1001
1304
|
|
|
1002
|
-
|
|
1305
|
+
materials.push( material );
|
|
1003
1306
|
|
|
1307
|
+
} else if ( elementSize === 2 ) {
|
|
1004
1308
|
|
|
1005
|
-
|
|
1309
|
+
if ( isConditionalSegments ) {
|
|
1006
1310
|
|
|
1007
|
-
|
|
1311
|
+
materials.push( material.userData.edgeMaterial.userData.conditionalEdgeMaterial );
|
|
1008
1312
|
|
|
1009
|
-
|
|
1313
|
+
} else {
|
|
1010
1314
|
|
|
1011
|
-
|
|
1315
|
+
materials.push( material.userData.edgeMaterial );
|
|
1012
1316
|
|
|
1013
|
-
|
|
1317
|
+
}
|
|
1014
1318
|
|
|
1015
1319
|
}
|
|
1016
1320
|
|
|
1017
|
-
|
|
1321
|
+
prevMaterial = elem.material;
|
|
1322
|
+
index0 = offset / 3;
|
|
1323
|
+
numGroupVerts = vertices.length;
|
|
1018
1324
|
|
|
1019
|
-
|
|
1020
|
-
code = lineParser.getToken();
|
|
1021
|
-
break;
|
|
1325
|
+
} else {
|
|
1022
1326
|
|
|
1023
|
-
|
|
1024
|
-
colour = lineParser.getToken();
|
|
1327
|
+
numGroupVerts += vertices.length;
|
|
1025
1328
|
|
|
1026
|
-
|
|
1329
|
+
}
|
|
1027
1330
|
|
|
1028
|
-
|
|
1331
|
+
offset += 3 * vertices.length;
|
|
1029
1332
|
|
|
1030
|
-
|
|
1333
|
+
}
|
|
1031
1334
|
|
|
1032
|
-
|
|
1335
|
+
if ( numGroupVerts > 0 ) {
|
|
1033
1336
|
|
|
1034
|
-
|
|
1337
|
+
bufferGeometry.addGroup( index0, Infinity, materials.length - 1 );
|
|
1035
1338
|
|
|
1036
|
-
|
|
1339
|
+
}
|
|
1037
1340
|
|
|
1038
|
-
|
|
1039
|
-
edgeColour = lineParser.getToken();
|
|
1341
|
+
bufferGeometry.setAttribute( 'position', new THREE.BufferAttribute( positions, 3 ) );
|
|
1040
1342
|
|
|
1041
|
-
|
|
1343
|
+
if ( normals !== null ) {
|
|
1042
1344
|
|
|
1043
|
-
|
|
1345
|
+
bufferGeometry.setAttribute( 'normal', new THREE.BufferAttribute( normals, 3 ) );
|
|
1044
1346
|
|
|
1045
|
-
|
|
1347
|
+
}
|
|
1046
1348
|
|
|
1047
|
-
|
|
1048
|
-
edgeMaterial = this.getMaterial( edgeColour );
|
|
1349
|
+
let object3d = null;
|
|
1049
1350
|
|
|
1050
|
-
|
|
1351
|
+
if ( elementSize === 2 ) {
|
|
1051
1352
|
|
|
1052
|
-
|
|
1353
|
+
object3d = new THREE.LineSegments( bufferGeometry, materials.length === 1 ? materials[ 0 ] : materials );
|
|
1053
1354
|
|
|
1054
|
-
|
|
1355
|
+
} else if ( elementSize === 3 ) {
|
|
1055
1356
|
|
|
1357
|
+
object3d = new THREE.Mesh( bufferGeometry, materials.length === 1 ? materials[ 0 ] : materials );
|
|
1056
1358
|
|
|
1057
|
-
|
|
1359
|
+
}
|
|
1058
1360
|
|
|
1059
|
-
|
|
1361
|
+
if ( isConditionalSegments ) {
|
|
1060
1362
|
|
|
1061
|
-
|
|
1363
|
+
object3d.isConditionalLine = true;
|
|
1364
|
+
const controlArray0 = new Float32Array( elements.length * 3 * 2 );
|
|
1365
|
+
const controlArray1 = new Float32Array( elements.length * 3 * 2 );
|
|
1366
|
+
const directionArray = new Float32Array( elements.length * 3 * 2 );
|
|
1062
1367
|
|
|
1063
|
-
|
|
1064
|
-
alpha = parseInt( lineParser.getToken() );
|
|
1368
|
+
for ( let i = 0, l = elements.length; i < l; i ++ ) {
|
|
1065
1369
|
|
|
1066
|
-
|
|
1370
|
+
const os = elements[ i ];
|
|
1371
|
+
const vertices = os.vertices;
|
|
1372
|
+
const controlPoints = os.controlPoints;
|
|
1373
|
+
const c0 = controlPoints[ 0 ];
|
|
1374
|
+
const c1 = controlPoints[ 1 ];
|
|
1375
|
+
const v0 = vertices[ 0 ];
|
|
1376
|
+
const v1 = vertices[ 1 ];
|
|
1377
|
+
const index = i * 3 * 2;
|
|
1378
|
+
controlArray0[ index + 0 ] = c0.x;
|
|
1379
|
+
controlArray0[ index + 1 ] = c0.y;
|
|
1380
|
+
controlArray0[ index + 2 ] = c0.z;
|
|
1381
|
+
controlArray0[ index + 3 ] = c0.x;
|
|
1382
|
+
controlArray0[ index + 4 ] = c0.y;
|
|
1383
|
+
controlArray0[ index + 5 ] = c0.z;
|
|
1384
|
+
controlArray1[ index + 0 ] = c1.x;
|
|
1385
|
+
controlArray1[ index + 1 ] = c1.y;
|
|
1386
|
+
controlArray1[ index + 2 ] = c1.z;
|
|
1387
|
+
controlArray1[ index + 3 ] = c1.x;
|
|
1388
|
+
controlArray1[ index + 4 ] = c1.y;
|
|
1389
|
+
controlArray1[ index + 5 ] = c1.z;
|
|
1390
|
+
directionArray[ index + 0 ] = v1.x - v0.x;
|
|
1391
|
+
directionArray[ index + 1 ] = v1.y - v0.y;
|
|
1392
|
+
directionArray[ index + 2 ] = v1.z - v0.z;
|
|
1393
|
+
directionArray[ index + 3 ] = v1.x - v0.x;
|
|
1394
|
+
directionArray[ index + 4 ] = v1.y - v0.y;
|
|
1395
|
+
directionArray[ index + 5 ] = v1.z - v0.z;
|
|
1067
1396
|
|
|
1068
|
-
|
|
1397
|
+
}
|
|
1069
1398
|
|
|
1070
|
-
|
|
1399
|
+
bufferGeometry.setAttribute( 'control0', new THREE.BufferAttribute( controlArray0, 3, false ) );
|
|
1400
|
+
bufferGeometry.setAttribute( 'control1', new THREE.BufferAttribute( controlArray1, 3, false ) );
|
|
1401
|
+
bufferGeometry.setAttribute( 'direction', new THREE.BufferAttribute( directionArray, 3, false ) );
|
|
1071
1402
|
|
|
1072
|
-
|
|
1403
|
+
}
|
|
1073
1404
|
|
|
1074
|
-
|
|
1405
|
+
return object3d;
|
|
1075
1406
|
|
|
1076
|
-
|
|
1407
|
+
} //
|
|
1077
1408
|
|
|
1078
|
-
}
|
|
1079
1409
|
|
|
1080
|
-
|
|
1410
|
+
class LDrawLoader extends THREE.Loader {
|
|
1081
1411
|
|
|
1082
|
-
|
|
1083
|
-
luminance = parseInt( lineParser.getToken() );
|
|
1412
|
+
constructor( manager ) {
|
|
1084
1413
|
|
|
1085
|
-
|
|
1414
|
+
super( manager ); // Array of THREE.Material
|
|
1415
|
+
|
|
1416
|
+
this.materials = []; // Not using THREE.Cache here because it returns the previous HTML error response instead of calling onError()
|
|
1417
|
+
// This also allows to handle the embedded text files ("0 FILE" lines)
|
|
1086
1418
|
|
|
1087
|
-
|
|
1419
|
+
this.parseCache = new LDrawParsedCache( 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.
|
|
1088
1420
|
|
|
1089
|
-
|
|
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)
|
|
1090
1423
|
|
|
1091
|
-
|
|
1092
|
-
break;
|
|
1424
|
+
this.setMaterials( [ this.parseColorMetaDirective( new LineParser( 'Main_Colour CODE 16 VALUE #FF8080 EDGE #333333' ) ), this.parseColorMetaDirective( new LineParser( 'Edge_Colour CODE 24 VALUE #A0A0A0 EDGE #333333' ) ) ] ); // If this flag is set to true the vertex normals will be smoothed.
|
|
1093
1425
|
|
|
1094
|
-
|
|
1095
|
-
finishType = FINISH_TYPE_CHROME;
|
|
1096
|
-
break;
|
|
1426
|
+
this.smoothNormals = true; // The path to load parts from the LDraw parts library from.
|
|
1097
1427
|
|
|
1098
|
-
|
|
1099
|
-
finishType = FINISH_TYPE_PEARLESCENT;
|
|
1100
|
-
break;
|
|
1428
|
+
this.partsLibraryPath = '';
|
|
1101
1429
|
|
|
1102
|
-
|
|
1103
|
-
finishType = FINISH_TYPE_RUBBER;
|
|
1104
|
-
break;
|
|
1430
|
+
}
|
|
1105
1431
|
|
|
1106
|
-
|
|
1107
|
-
finishType = FINISH_TYPE_MATTE_METALLIC;
|
|
1108
|
-
break;
|
|
1432
|
+
setPartsLibraryPath( path ) {
|
|
1109
1433
|
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
break;
|
|
1434
|
+
this.partsLibraryPath = path;
|
|
1435
|
+
return this;
|
|
1113
1436
|
|
|
1114
|
-
|
|
1115
|
-
// Not implemented
|
|
1116
|
-
lineParser.setToEnd();
|
|
1117
|
-
break;
|
|
1437
|
+
}
|
|
1118
1438
|
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1439
|
+
async preloadMaterials( url ) {
|
|
1440
|
+
|
|
1441
|
+
const fileLoader = new THREE.FileLoader( this.manager );
|
|
1442
|
+
fileLoader.setPath( this.path );
|
|
1443
|
+
fileLoader.setRequestHeader( this.requestHeader );
|
|
1444
|
+
fileLoader.setWithCredentials( this.withCredentials );
|
|
1445
|
+
const text = await fileLoader.loadAsync( url );
|
|
1446
|
+
const colorLineRegex = /^0 !COLOUR/;
|
|
1447
|
+
const lines = text.split( /[\n\r]/g );
|
|
1448
|
+
const materials = [];
|
|
1449
|
+
|
|
1450
|
+
for ( let i = 0, l = lines.length; i < l; i ++ ) {
|
|
1451
|
+
|
|
1452
|
+
const line = lines[ i ];
|
|
1453
|
+
|
|
1454
|
+
if ( colorLineRegex.test( line ) ) {
|
|
1455
|
+
|
|
1456
|
+
const directive = line.replace( colorLineRegex, '' );
|
|
1457
|
+
const material = this.parseColorMetaDirective( new LineParser( directive ) );
|
|
1458
|
+
materials.push( material );
|
|
1122
1459
|
|
|
1123
1460
|
}
|
|
1124
1461
|
|
|
1125
1462
|
}
|
|
1126
1463
|
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
switch ( finishType ) {
|
|
1464
|
+
this.setMaterials( materials );
|
|
1130
1465
|
|
|
1131
|
-
|
|
1132
|
-
material = new THREE.MeshStandardMaterial( {
|
|
1133
|
-
color: colour,
|
|
1134
|
-
roughness: 0.3,
|
|
1135
|
-
metalness: 0
|
|
1136
|
-
} );
|
|
1137
|
-
break;
|
|
1466
|
+
}
|
|
1138
1467
|
|
|
1139
|
-
|
|
1140
|
-
// Try to imitate pearlescency by making the surface glossy
|
|
1141
|
-
material = new THREE.MeshStandardMaterial( {
|
|
1142
|
-
color: colour,
|
|
1143
|
-
roughness: 0.3,
|
|
1144
|
-
metalness: 0.25
|
|
1145
|
-
} );
|
|
1146
|
-
break;
|
|
1468
|
+
load( url, onLoad, onProgress, onError ) {
|
|
1147
1469
|
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
metalness: 1
|
|
1154
|
-
} );
|
|
1155
|
-
break;
|
|
1470
|
+
const fileLoader = new THREE.FileLoader( this.manager );
|
|
1471
|
+
fileLoader.setPath( this.path );
|
|
1472
|
+
fileLoader.setRequestHeader( this.requestHeader );
|
|
1473
|
+
fileLoader.setWithCredentials( this.withCredentials );
|
|
1474
|
+
fileLoader.load( url, text => {
|
|
1156
1475
|
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
material = new THREE.MeshStandardMaterial( {
|
|
1160
|
-
color: colour,
|
|
1161
|
-
roughness: 0.9,
|
|
1162
|
-
metalness: 0
|
|
1163
|
-
} );
|
|
1164
|
-
break;
|
|
1476
|
+
const parsedInfo = this.parseCache.parse( text );
|
|
1477
|
+
this.processObject( parsedInfo, null, url, this.rootParseScope ).then( function ( result ) {
|
|
1165
1478
|
|
|
1166
|
-
|
|
1167
|
-
// Brushed metal finish
|
|
1168
|
-
material = new THREE.MeshStandardMaterial( {
|
|
1169
|
-
color: colour,
|
|
1170
|
-
roughness: 0.8,
|
|
1171
|
-
metalness: 0.4
|
|
1172
|
-
} );
|
|
1173
|
-
break;
|
|
1479
|
+
onLoad( result.groupObject );
|
|
1174
1480
|
|
|
1175
|
-
|
|
1176
|
-
// Average metal finish
|
|
1177
|
-
material = new THREE.MeshStandardMaterial( {
|
|
1178
|
-
color: colour,
|
|
1179
|
-
roughness: 0.2,
|
|
1180
|
-
metalness: 0.85
|
|
1181
|
-
} );
|
|
1182
|
-
break;
|
|
1481
|
+
} );
|
|
1183
1482
|
|
|
1184
|
-
|
|
1185
|
-
// Should not happen
|
|
1186
|
-
break;
|
|
1483
|
+
}, onProgress, onError );
|
|
1187
1484
|
|
|
1188
|
-
|
|
1485
|
+
}
|
|
1189
1486
|
|
|
1190
|
-
|
|
1191
|
-
material.premultipliedAlpha = true;
|
|
1192
|
-
material.opacity = alpha;
|
|
1193
|
-
material.depthWrite = ! isTransparent;
|
|
1194
|
-
material.polygonOffset = true;
|
|
1195
|
-
material.polygonOffsetFactor = 1;
|
|
1487
|
+
parse( text, path, onLoad ) {
|
|
1196
1488
|
|
|
1197
|
-
|
|
1489
|
+
// Async parse. This function calls onParse with the parsed THREE.Object3D as parameter
|
|
1490
|
+
const parsedInfo = this.parseCache.parse( text );
|
|
1491
|
+
this.processObject( parsedInfo, null, path, this.rootParseScope ).then( function ( result ) {
|
|
1198
1492
|
|
|
1199
|
-
|
|
1493
|
+
onLoad( result.groupObject );
|
|
1200
1494
|
|
|
1201
|
-
}
|
|
1495
|
+
} );
|
|
1202
1496
|
|
|
1203
|
-
|
|
1497
|
+
}
|
|
1204
1498
|
|
|
1205
|
-
|
|
1206
|
-
edgeMaterial = new THREE.LineBasicMaterial( {
|
|
1207
|
-
color: edgeColour,
|
|
1208
|
-
transparent: isTransparent,
|
|
1209
|
-
opacity: alpha,
|
|
1210
|
-
depthWrite: ! isTransparent
|
|
1211
|
-
} );
|
|
1212
|
-
edgeMaterial.userData.code = code;
|
|
1213
|
-
edgeMaterial.name = name + ' - Edge'; // This is the material used for conditional edges
|
|
1499
|
+
setMaterials( materials ) {
|
|
1214
1500
|
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
opacity: alpha
|
|
1221
|
-
} );
|
|
1501
|
+
// Clears parse scopes stack, adds new scope with material library
|
|
1502
|
+
this.rootParseScope = this.newParseScopeLevel( materials );
|
|
1503
|
+
this.rootParseScope.isFromParse = false;
|
|
1504
|
+
this.materials = materials;
|
|
1505
|
+
return this;
|
|
1222
1506
|
|
|
1223
|
-
|
|
1507
|
+
}
|
|
1224
1508
|
|
|
1225
|
-
|
|
1226
|
-
material.name = name;
|
|
1227
|
-
material.userData.edgeMaterial = edgeMaterial;
|
|
1228
|
-
return material;
|
|
1509
|
+
setFileMap( fileMap ) {
|
|
1229
1510
|
|
|
1230
|
-
|
|
1511
|
+
this.fileMap = fileMap;
|
|
1512
|
+
return this;
|
|
1231
1513
|
|
|
1514
|
+
}
|
|
1232
1515
|
|
|
1233
|
-
|
|
1516
|
+
newParseScopeLevel( materials = null, parentScope = null ) {
|
|
1234
1517
|
|
|
1235
|
-
//
|
|
1236
|
-
const
|
|
1237
|
-
const parentParseScope = currentParseScope.parentScope; // Main colour codes passed to this subobject (or default codes 16 and 24 if it is the root object)
|
|
1518
|
+
// Adds a new scope level, assign materials to it and returns it
|
|
1519
|
+
const matLib = {};
|
|
1238
1520
|
|
|
1239
|
-
|
|
1240
|
-
const mainEdgeColourCode = currentParseScope.mainEdgeColourCode; // Parse result variables
|
|
1521
|
+
if ( materials ) {
|
|
1241
1522
|
|
|
1242
|
-
|
|
1243
|
-
let lineSegments;
|
|
1244
|
-
let conditionalSegments;
|
|
1245
|
-
const subobjects = [];
|
|
1246
|
-
let category = null;
|
|
1247
|
-
let keywords = null;
|
|
1523
|
+
for ( let i = 0, n = materials.length; i < n; i ++ ) {
|
|
1248
1524
|
|
|
1249
|
-
|
|
1525
|
+
const material = materials[ i ];
|
|
1526
|
+
matLib[ material.userData.code ] = material;
|
|
1250
1527
|
|
|
1251
|
-
|
|
1252
|
-
text = text.replace( /\r\n/g, '\n' );
|
|
1528
|
+
}
|
|
1253
1529
|
|
|
1254
1530
|
}
|
|
1255
1531
|
|
|
1256
|
-
const
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1532
|
+
const newParseScope = {
|
|
1533
|
+
parentScope: parentScope,
|
|
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;
|
|
1268
1561
|
|
|
1269
|
-
|
|
1562
|
+
}
|
|
1270
1563
|
|
|
1271
|
-
|
|
1272
|
-
let colourCode = lineParser.getToken();
|
|
1564
|
+
addMaterial( material, parseScope ) {
|
|
1273
1565
|
|
|
1274
|
-
|
|
1566
|
+
// 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 = parseScope.lib;
|
|
1275
1568
|
|
|
1276
|
-
|
|
1569
|
+
if ( ! matLib[ material.userData.code ] ) {
|
|
1277
1570
|
|
|
1278
|
-
|
|
1571
|
+
this.materials.push( material );
|
|
1279
1572
|
|
|
1280
|
-
|
|
1573
|
+
}
|
|
1281
1574
|
|
|
1282
|
-
|
|
1575
|
+
matLib[ material.userData.code ] = material;
|
|
1576
|
+
return this;
|
|
1283
1577
|
|
|
1284
|
-
|
|
1578
|
+
}
|
|
1285
1579
|
|
|
1286
|
-
|
|
1580
|
+
getMaterial( colorCode, parseScope = this.rootParseScope ) {
|
|
1287
1581
|
|
|
1288
|
-
|
|
1582
|
+
// Given a color code search its material in the parse scopes stack
|
|
1583
|
+
if ( colorCode.startsWith( '0x2' ) ) {
|
|
1289
1584
|
|
|
1290
|
-
|
|
1585
|
+
// Special 'direct' material value (RGB color)
|
|
1586
|
+
const color = colorCode.substring( 3 );
|
|
1587
|
+
return this.parseColorMetaDirective( new LineParser( 'Direct_Color_' + color + ' CODE -1 VALUE #' + color + ' EDGE #' + color + '' ) );
|
|
1291
1588
|
|
|
1292
|
-
|
|
1589
|
+
}
|
|
1293
1590
|
|
|
1294
|
-
|
|
1591
|
+
while ( parseScope ) {
|
|
1295
1592
|
|
|
1296
|
-
|
|
1593
|
+
const material = parseScope.lib[ colorCode ];
|
|
1297
1594
|
|
|
1298
|
-
|
|
1595
|
+
if ( material ) {
|
|
1299
1596
|
|
|
1300
|
-
|
|
1597
|
+
return material;
|
|
1301
1598
|
|
|
1302
|
-
|
|
1599
|
+
} else {
|
|
1303
1600
|
|
|
1304
|
-
|
|
1601
|
+
parseScope = parseScope.parentScope;
|
|
1305
1602
|
|
|
1306
1603
|
}
|
|
1307
1604
|
|
|
1308
|
-
|
|
1605
|
+
} // Material was not found
|
|
1309
1606
|
|
|
1310
|
-
} // Parse all line commands
|
|
1311
1607
|
|
|
1608
|
+
return null;
|
|
1312
1609
|
|
|
1313
|
-
|
|
1610
|
+
}
|
|
1314
1611
|
|
|
1315
|
-
|
|
1316
|
-
if ( line.length === 0 ) continue;
|
|
1612
|
+
parseColorMetaDirective( lineParser ) {
|
|
1317
1613
|
|
|
1318
|
-
|
|
1614
|
+
// Parses a color definition and returns a THREE.Material
|
|
1615
|
+
let code = null; // Triangle and line colors
|
|
1319
1616
|
|
|
1320
|
-
|
|
1617
|
+
let color = 0xFF00FF;
|
|
1618
|
+
let edgeColor = 0xFF00FF; // Transparency
|
|
1321
1619
|
|
|
1322
|
-
|
|
1323
|
-
|
|
1620
|
+
let alpha = 1;
|
|
1621
|
+
let isTransparent = false; // Self-illumination:
|
|
1324
1622
|
|
|
1325
|
-
|
|
1326
|
-
|
|
1623
|
+
let luminance = 0;
|
|
1624
|
+
let finishType = FINISH_TYPE_DEFAULT;
|
|
1625
|
+
let edgeMaterial = null;
|
|
1626
|
+
const name = lineParser.getToken();
|
|
1327
1627
|
|
|
1328
|
-
|
|
1628
|
+
if ( ! name ) {
|
|
1329
1629
|
|
|
1330
|
-
|
|
1630
|
+
throw new Error( 'LDrawLoader: Material name was expected after "!COLOUR tag' + lineParser.getLineNumberString() + '.' );
|
|
1331
1631
|
|
|
1332
|
-
|
|
1632
|
+
} // Parse tag tokens and their parameters
|
|
1333
1633
|
|
|
1334
|
-
|
|
1634
|
+
|
|
1635
|
+
let token = null;
|
|
1636
|
+
|
|
1637
|
+
while ( true ) {
|
|
1638
|
+
|
|
1639
|
+
token = lineParser.getToken();
|
|
1640
|
+
|
|
1641
|
+
if ( ! token ) {
|
|
1642
|
+
|
|
1643
|
+
break;
|
|
1335
1644
|
|
|
1336
1645
|
}
|
|
1337
1646
|
|
|
1338
|
-
|
|
1339
|
-
lp.seekNonSpace();
|
|
1647
|
+
switch ( token.toUpperCase() ) {
|
|
1340
1648
|
|
|
1341
|
-
|
|
1649
|
+
case 'CODE':
|
|
1650
|
+
code = lineParser.getToken();
|
|
1651
|
+
break;
|
|
1342
1652
|
|
|
1343
|
-
|
|
1344
|
-
|
|
1653
|
+
case 'VALUE':
|
|
1654
|
+
color = lineParser.getToken();
|
|
1345
1655
|
|
|
1346
|
-
|
|
1656
|
+
if ( color.startsWith( '0x' ) ) {
|
|
1347
1657
|
|
|
1658
|
+
color = '#' + color.substring( 2 );
|
|
1348
1659
|
|
|
1349
|
-
|
|
1350
|
-
let material;
|
|
1351
|
-
let segment;
|
|
1352
|
-
let inverted;
|
|
1353
|
-
let ccw;
|
|
1354
|
-
let doubleSided;
|
|
1355
|
-
let v0, v1, v2, v3, c0, c1, faceNormal;
|
|
1660
|
+
} else if ( ! color.startsWith( '#' ) ) {
|
|
1356
1661
|
|
|
1357
|
-
|
|
1662
|
+
throw new Error( 'LDrawLoader: Invalid color while parsing material' + lineParser.getLineNumberString() + '.' );
|
|
1358
1663
|
|
|
1359
|
-
|
|
1360
|
-
case '0':
|
|
1361
|
-
// Parse meta directive
|
|
1362
|
-
const meta = lp.getToken();
|
|
1664
|
+
}
|
|
1363
1665
|
|
|
1364
|
-
|
|
1666
|
+
break;
|
|
1365
1667
|
|
|
1366
|
-
|
|
1668
|
+
case 'EDGE':
|
|
1669
|
+
edgeColor = lineParser.getToken();
|
|
1367
1670
|
|
|
1368
|
-
|
|
1369
|
-
type = lp.getToken();
|
|
1370
|
-
currentParseScope.type = type; // If the scale of the object is negated then the triangle winding order
|
|
1371
|
-
// needs to be flipped.
|
|
1671
|
+
if ( edgeColor.startsWith( '0x' ) ) {
|
|
1372
1672
|
|
|
1373
|
-
|
|
1673
|
+
edgeColor = '#' + edgeColor.substring( 2 );
|
|
1374
1674
|
|
|
1375
|
-
|
|
1675
|
+
} else if ( ! edgeColor.startsWith( '#' ) ) {
|
|
1376
1676
|
|
|
1377
|
-
|
|
1677
|
+
// Try to see if edge color is a color code
|
|
1678
|
+
edgeMaterial = this.getMaterial( edgeColor );
|
|
1378
1679
|
|
|
1379
|
-
|
|
1380
|
-
lineSegments = currentParseScope.lineSegments;
|
|
1381
|
-
conditionalSegments = currentParseScope.conditionalSegments;
|
|
1382
|
-
break;
|
|
1680
|
+
if ( ! edgeMaterial ) {
|
|
1383
1681
|
|
|
1384
|
-
|
|
1385
|
-
material = this.parseColourMetaDirective( lp );
|
|
1682
|
+
throw new Error( 'LDrawLoader: Invalid edge color while parsing material' + lineParser.getLineNumberString() + '.' );
|
|
1386
1683
|
|
|
1387
|
-
|
|
1684
|
+
} // Get the edge material for this triangle material
|
|
1388
1685
|
|
|
1389
|
-
this.addMaterial( material, parseScope );
|
|
1390
1686
|
|
|
1391
|
-
|
|
1687
|
+
edgeMaterial = edgeMaterial.userData.edgeMaterial;
|
|
1392
1688
|
|
|
1393
|
-
|
|
1689
|
+
}
|
|
1394
1690
|
|
|
1395
|
-
|
|
1691
|
+
break;
|
|
1396
1692
|
|
|
1397
|
-
|
|
1693
|
+
case 'ALPHA':
|
|
1694
|
+
alpha = parseInt( lineParser.getToken() );
|
|
1398
1695
|
|
|
1399
|
-
|
|
1400
|
-
category = lp.getToken();
|
|
1401
|
-
break;
|
|
1696
|
+
if ( isNaN( alpha ) ) {
|
|
1402
1697
|
|
|
1403
|
-
|
|
1404
|
-
const newKeywords = lp.getRemainingString().split( ',' );
|
|
1698
|
+
throw new Error( 'LDrawLoader: Invalid alpha value in material definition' + lineParser.getLineNumberString() + '.' );
|
|
1405
1699
|
|
|
1406
|
-
|
|
1700
|
+
}
|
|
1407
1701
|
|
|
1408
|
-
|
|
1702
|
+
alpha = Math.max( 0, Math.min( 1, alpha / 255 ) );
|
|
1409
1703
|
|
|
1410
|
-
|
|
1704
|
+
if ( alpha < 1 ) {
|
|
1411
1705
|
|
|
1412
|
-
|
|
1706
|
+
isTransparent = true;
|
|
1413
1707
|
|
|
1414
|
-
|
|
1708
|
+
}
|
|
1415
1709
|
|
|
1416
|
-
|
|
1710
|
+
break;
|
|
1417
1711
|
|
|
1418
|
-
|
|
1712
|
+
case 'LUMINANCE':
|
|
1713
|
+
luminance = parseInt( lineParser.getToken() );
|
|
1419
1714
|
|
|
1420
|
-
|
|
1715
|
+
if ( isNaN( luminance ) ) {
|
|
1421
1716
|
|
|
1422
|
-
|
|
1717
|
+
throw new Error( 'LDrawLoader: Invalid luminance value in material definition' + LineParser.getLineNumberString() + '.' );
|
|
1423
1718
|
|
|
1424
|
-
|
|
1425
|
-
if ( lineIndex > 0 ) {
|
|
1719
|
+
}
|
|
1426
1720
|
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1721
|
+
luminance = Math.max( 0, Math.min( 1, luminance / 255 ) );
|
|
1722
|
+
break;
|
|
1723
|
+
|
|
1724
|
+
case 'CHROME':
|
|
1725
|
+
finishType = FINISH_TYPE_CHROME;
|
|
1726
|
+
break;
|
|
1727
|
+
|
|
1728
|
+
case 'PEARLESCENT':
|
|
1729
|
+
finishType = FINISH_TYPE_PEARLESCENT;
|
|
1730
|
+
break;
|
|
1731
|
+
|
|
1732
|
+
case 'RUBBER':
|
|
1733
|
+
finishType = FINISH_TYPE_RUBBER;
|
|
1734
|
+
break;
|
|
1433
1735
|
|
|
1434
|
-
|
|
1736
|
+
case 'MATTE_METALLIC':
|
|
1737
|
+
finishType = FINISH_TYPE_MATTE_METALLIC;
|
|
1738
|
+
break;
|
|
1435
1739
|
|
|
1436
|
-
|
|
1740
|
+
case 'METAL':
|
|
1741
|
+
finishType = FINISH_TYPE_METAL;
|
|
1742
|
+
break;
|
|
1437
1743
|
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1744
|
+
case 'MATERIAL':
|
|
1745
|
+
// Not implemented
|
|
1746
|
+
lineParser.setToEnd();
|
|
1747
|
+
break;
|
|
1441
1748
|
|
|
1442
|
-
|
|
1749
|
+
default:
|
|
1750
|
+
throw new Error( 'LDrawLoader: Unknown token "' + token + '" while parsing material' + lineParser.getLineNumberString() + '.' );
|
|
1443
1751
|
|
|
1444
|
-
|
|
1752
|
+
}
|
|
1445
1753
|
|
|
1446
|
-
|
|
1447
|
-
case 'NOCERTIFY':
|
|
1448
|
-
bfcCertified = token === 'CERTIFY';
|
|
1449
|
-
bfcCCW = true;
|
|
1450
|
-
break;
|
|
1754
|
+
}
|
|
1451
1755
|
|
|
1452
|
-
|
|
1453
|
-
case 'CCW':
|
|
1454
|
-
bfcCCW = token === 'CCW';
|
|
1455
|
-
break;
|
|
1756
|
+
let material = null;
|
|
1456
1757
|
|
|
1457
|
-
|
|
1458
|
-
bfcInverted = true;
|
|
1459
|
-
break;
|
|
1758
|
+
switch ( finishType ) {
|
|
1460
1759
|
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1760
|
+
case FINISH_TYPE_DEFAULT:
|
|
1761
|
+
material = new THREE.MeshStandardMaterial( {
|
|
1762
|
+
color: color,
|
|
1763
|
+
roughness: 0.3,
|
|
1764
|
+
metalness: 0
|
|
1765
|
+
} );
|
|
1766
|
+
break;
|
|
1465
1767
|
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1768
|
+
case FINISH_TYPE_PEARLESCENT:
|
|
1769
|
+
// Try to imitate pearlescency by making the surface glossy
|
|
1770
|
+
material = new THREE.MeshStandardMaterial( {
|
|
1771
|
+
color: color,
|
|
1772
|
+
roughness: 0.3,
|
|
1773
|
+
metalness: 0.25
|
|
1774
|
+
} );
|
|
1775
|
+
break;
|
|
1469
1776
|
|
|
1470
|
-
|
|
1777
|
+
case FINISH_TYPE_CHROME:
|
|
1778
|
+
// Mirror finish surface
|
|
1779
|
+
material = new THREE.MeshStandardMaterial( {
|
|
1780
|
+
color: color,
|
|
1781
|
+
roughness: 0,
|
|
1782
|
+
metalness: 1
|
|
1783
|
+
} );
|
|
1784
|
+
break;
|
|
1471
1785
|
|
|
1472
|
-
|
|
1786
|
+
case FINISH_TYPE_RUBBER:
|
|
1787
|
+
// Rubber finish
|
|
1788
|
+
material = new THREE.MeshStandardMaterial( {
|
|
1789
|
+
color: color,
|
|
1790
|
+
roughness: 0.9,
|
|
1791
|
+
metalness: 0
|
|
1792
|
+
} );
|
|
1793
|
+
break;
|
|
1473
1794
|
|
|
1474
|
-
|
|
1795
|
+
case FINISH_TYPE_MATTE_METALLIC:
|
|
1796
|
+
// Brushed metal finish
|
|
1797
|
+
material = new THREE.MeshStandardMaterial( {
|
|
1798
|
+
color: color,
|
|
1799
|
+
roughness: 0.8,
|
|
1800
|
+
metalness: 0.4
|
|
1801
|
+
} );
|
|
1802
|
+
break;
|
|
1475
1803
|
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1804
|
+
case FINISH_TYPE_METAL:
|
|
1805
|
+
// Average metal finish
|
|
1806
|
+
material = new THREE.MeshStandardMaterial( {
|
|
1807
|
+
color: color,
|
|
1808
|
+
roughness: 0.2,
|
|
1809
|
+
metalness: 0.85
|
|
1810
|
+
} );
|
|
1811
|
+
break;
|
|
1479
1812
|
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1813
|
+
default:
|
|
1814
|
+
// Should not happen
|
|
1815
|
+
break;
|
|
1483
1816
|
|
|
1484
|
-
|
|
1817
|
+
}
|
|
1485
1818
|
|
|
1486
|
-
|
|
1819
|
+
material.transparent = isTransparent;
|
|
1820
|
+
material.premultipliedAlpha = true;
|
|
1821
|
+
material.opacity = alpha;
|
|
1822
|
+
material.depthWrite = ! isTransparent;
|
|
1823
|
+
material.polygonOffset = true;
|
|
1824
|
+
material.polygonOffsetFactor = 1;
|
|
1487
1825
|
|
|
1488
|
-
|
|
1489
|
-
// Line type 1: Sub-object file
|
|
1826
|
+
if ( luminance !== 0 ) {
|
|
1490
1827
|
|
|
1491
|
-
|
|
1492
|
-
material = parseColourCode( lp );
|
|
1493
|
-
const posX = parseFloat( lp.getToken() );
|
|
1494
|
-
const posY = parseFloat( lp.getToken() );
|
|
1495
|
-
const posZ = parseFloat( lp.getToken() );
|
|
1496
|
-
const m0 = parseFloat( lp.getToken() );
|
|
1497
|
-
const m1 = parseFloat( lp.getToken() );
|
|
1498
|
-
const m2 = parseFloat( lp.getToken() );
|
|
1499
|
-
const m3 = parseFloat( lp.getToken() );
|
|
1500
|
-
const m4 = parseFloat( lp.getToken() );
|
|
1501
|
-
const m5 = parseFloat( lp.getToken() );
|
|
1502
|
-
const m6 = parseFloat( lp.getToken() );
|
|
1503
|
-
const m7 = parseFloat( lp.getToken() );
|
|
1504
|
-
const m8 = parseFloat( lp.getToken() );
|
|
1505
|
-
const matrix = new THREE.Matrix4().set( m0, m1, m2, posX, m3, m4, m5, posY, m6, m7, m8, posZ, 0, 0, 0, 1 );
|
|
1506
|
-
let fileName = lp.getRemainingString().trim().replace( /\\/g, '/' );
|
|
1828
|
+
material.emissive.set( material.color ).multiplyScalar( luminance );
|
|
1507
1829
|
|
|
1508
|
-
|
|
1830
|
+
}
|
|
1509
1831
|
|
|
1510
|
-
|
|
1511
|
-
fileName = scope.fileMap[ fileName ];
|
|
1832
|
+
if ( ! edgeMaterial ) {
|
|
1512
1833
|
|
|
1513
|
-
|
|
1834
|
+
// This is the material used for edges
|
|
1835
|
+
edgeMaterial = new THREE.LineBasicMaterial( {
|
|
1836
|
+
color: edgeColor,
|
|
1837
|
+
transparent: isTransparent,
|
|
1838
|
+
opacity: alpha,
|
|
1839
|
+
depthWrite: ! isTransparent
|
|
1840
|
+
} );
|
|
1841
|
+
edgeMaterial.userData.code = code;
|
|
1842
|
+
edgeMaterial.name = name + ' - Edge'; // This is the material used for conditional edges
|
|
1514
1843
|
|
|
1515
|
-
|
|
1516
|
-
|
|
1844
|
+
edgeMaterial.userData.conditionalEdgeMaterial = new LDrawConditionalLineMaterial( {
|
|
1845
|
+
fog: true,
|
|
1846
|
+
transparent: isTransparent,
|
|
1847
|
+
depthWrite: ! isTransparent,
|
|
1848
|
+
color: edgeColor,
|
|
1849
|
+
opacity: alpha
|
|
1850
|
+
} );
|
|
1517
1851
|
|
|
1518
|
-
|
|
1852
|
+
}
|
|
1519
1853
|
|
|
1520
|
-
|
|
1854
|
+
material.userData.code = code;
|
|
1855
|
+
material.name = name;
|
|
1856
|
+
material.userData.edgeMaterial = edgeMaterial;
|
|
1857
|
+
return material;
|
|
1521
1858
|
|
|
1522
|
-
|
|
1859
|
+
} //
|
|
1523
1860
|
|
|
1524
|
-
}
|
|
1525
1861
|
|
|
1526
|
-
|
|
1862
|
+
objectParse( info, parseScope ) {
|
|
1527
1863
|
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
fileName: fileName,
|
|
1532
|
-
inverted: bfcInverted !== currentParseScope.inverted,
|
|
1533
|
-
startingConstructionStep: startingConstructionStep
|
|
1534
|
-
} );
|
|
1535
|
-
bfcInverted = false;
|
|
1536
|
-
break;
|
|
1537
|
-
// Line type 2: Line segment
|
|
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)
|
|
1538
1867
|
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
v0 = parseVector( lp );
|
|
1542
|
-
v1 = parseVector( lp );
|
|
1543
|
-
segment = {
|
|
1544
|
-
material: material.userData.edgeMaterial,
|
|
1545
|
-
colourCode: material.userData.code,
|
|
1546
|
-
v0: v0,
|
|
1547
|
-
v1: v1,
|
|
1548
|
-
vertices: [ v0, v1 ]
|
|
1549
|
-
};
|
|
1550
|
-
lineSegments.push( segment );
|
|
1551
|
-
break;
|
|
1552
|
-
// Line type 5: Conditional Line segment
|
|
1868
|
+
const mainColorCode = currentParseScope.mainColorCode;
|
|
1869
|
+
const mainEdgeColorCode = currentParseScope.mainEdgeColorCode;
|
|
1553
1870
|
|
|
1554
|
-
|
|
1555
|
-
material = parseColourCode( lp, true );
|
|
1556
|
-
v0 = parseVector( lp );
|
|
1557
|
-
v1 = parseVector( lp );
|
|
1558
|
-
c0 = parseVector( lp );
|
|
1559
|
-
c1 = parseVector( lp );
|
|
1560
|
-
segment = {
|
|
1561
|
-
material: material.userData.edgeMaterial.userData.conditionalEdgeMaterial,
|
|
1562
|
-
colourCode: material.userData.code,
|
|
1563
|
-
vertices: [ v0, v1 ],
|
|
1564
|
-
controlPoints: [ c0, c1 ]
|
|
1565
|
-
};
|
|
1566
|
-
conditionalSegments.push( segment );
|
|
1567
|
-
break;
|
|
1568
|
-
// Line type 3: Triangle
|
|
1871
|
+
const parseColorCode = ( colorCode, forEdge ) => {
|
|
1569
1872
|
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
inverted = currentParseScope.inverted;
|
|
1573
|
-
ccw = bfcCCW !== inverted;
|
|
1574
|
-
doubleSided = ! bfcCertified || ! bfcCull;
|
|
1873
|
+
// Parses next color code and returns a THREE.Material
|
|
1874
|
+
if ( ! forEdge && colorCode === '16' ) {
|
|
1575
1875
|
|
|
1576
|
-
|
|
1876
|
+
colorCode = mainColorCode;
|
|
1577
1877
|
|
|
1578
|
-
|
|
1579
|
-
v1 = parseVector( lp );
|
|
1580
|
-
v2 = parseVector( lp );
|
|
1878
|
+
}
|
|
1581
1879
|
|
|
1582
|
-
|
|
1880
|
+
if ( forEdge && colorCode === '24' ) {
|
|
1583
1881
|
|
|
1584
|
-
|
|
1585
|
-
v1 = parseVector( lp );
|
|
1586
|
-
v0 = parseVector( lp );
|
|
1882
|
+
colorCode = mainEdgeColorCode;
|
|
1587
1883
|
|
|
1588
|
-
|
|
1884
|
+
}
|
|
1589
1885
|
|
|
1590
|
-
|
|
1886
|
+
const material = this.getMaterial( colorCode, currentParseScope );
|
|
1591
1887
|
|
|
1592
|
-
|
|
1888
|
+
if ( ! material ) {
|
|
1593
1889
|
|
|
1594
|
-
|
|
1595
|
-
faces.push( {
|
|
1596
|
-
material: material,
|
|
1597
|
-
colourCode: material.userData.code,
|
|
1598
|
-
faceNormal: faceNormal,
|
|
1599
|
-
vertices: [ v0, v1, v2 ],
|
|
1600
|
-
normals: [ null, null, null ]
|
|
1601
|
-
} );
|
|
1602
|
-
currentParseScope.totalFaces ++;
|
|
1890
|
+
throw new Error( 'LDrawLoader: Unknown color code "' + colorCode + '" is used but it was not defined previously.' );
|
|
1603
1891
|
|
|
1604
|
-
|
|
1892
|
+
}
|
|
1605
1893
|
|
|
1606
|
-
|
|
1607
|
-
material: material,
|
|
1608
|
-
colourCode: material.userData.code,
|
|
1609
|
-
faceNormal: faceNormal,
|
|
1610
|
-
vertices: [ v2, v1, v0 ],
|
|
1611
|
-
normals: [ null, null, null ]
|
|
1612
|
-
} );
|
|
1613
|
-
currentParseScope.totalFaces ++;
|
|
1894
|
+
return material;
|
|
1614
1895
|
|
|
1615
|
-
|
|
1896
|
+
};
|
|
1616
1897
|
|
|
1617
|
-
|
|
1618
|
-
|
|
1898
|
+
const faces = info.faces;
|
|
1899
|
+
const lineSegments = info.lineSegments;
|
|
1900
|
+
const conditionalSegments = info.conditionalSegments;
|
|
1901
|
+
const materials = info.materials;
|
|
1619
1902
|
|
|
1620
|
-
|
|
1621
|
-
material = parseColourCode( lp );
|
|
1622
|
-
inverted = currentParseScope.inverted;
|
|
1623
|
-
ccw = bfcCCW !== inverted;
|
|
1624
|
-
doubleSided = ! bfcCertified || ! bfcCull;
|
|
1903
|
+
if ( currentParseScope.inverted ) {
|
|
1625
1904
|
|
|
1626
|
-
|
|
1905
|
+
faces.reverse();
|
|
1627
1906
|
|
|
1628
|
-
|
|
1629
|
-
v1 = parseVector( lp );
|
|
1630
|
-
v2 = parseVector( lp );
|
|
1631
|
-
v3 = parseVector( lp );
|
|
1907
|
+
}
|
|
1632
1908
|
|
|
1633
|
-
|
|
1909
|
+
for ( const colorCode in materials ) {
|
|
1634
1910
|
|
|
1635
|
-
|
|
1636
|
-
v2 = parseVector( lp );
|
|
1637
|
-
v1 = parseVector( lp );
|
|
1638
|
-
v0 = parseVector( lp );
|
|
1911
|
+
this.addMaterial( materials[ colorCode ], currentParseScope );
|
|
1639
1912
|
|
|
1640
|
-
|
|
1913
|
+
}
|
|
1641
1914
|
|
|
1642
|
-
|
|
1915
|
+
for ( let i = 0, l = faces.length; i < l; i ++ ) {
|
|
1643
1916
|
|
|
1644
|
-
|
|
1917
|
+
const face = faces[ i ];
|
|
1645
1918
|
|
|
1646
|
-
|
|
1647
|
-
// account for the doubling of vertices later when smoothing normals.
|
|
1919
|
+
if ( face.material === null ) {
|
|
1648
1920
|
|
|
1649
|
-
|
|
1650
|
-
material: material,
|
|
1651
|
-
colourCode: material.userData.code,
|
|
1652
|
-
faceNormal: faceNormal,
|
|
1653
|
-
vertices: [ v0, v1, v2, v3 ],
|
|
1654
|
-
normals: [ null, null, null, null ]
|
|
1655
|
-
} );
|
|
1656
|
-
currentParseScope.totalFaces += 2;
|
|
1921
|
+
face.material = parseColorCode( face.colorCode, false );
|
|
1657
1922
|
|
|
1658
|
-
|
|
1923
|
+
}
|
|
1659
1924
|
|
|
1660
|
-
|
|
1661
|
-
material: material,
|
|
1662
|
-
colourCode: material.userData.code,
|
|
1663
|
-
faceNormal: faceNormal,
|
|
1664
|
-
vertices: [ v3, v2, v1, v0 ],
|
|
1665
|
-
normals: [ null, null, null, null ]
|
|
1666
|
-
} );
|
|
1667
|
-
currentParseScope.totalFaces += 2;
|
|
1925
|
+
}
|
|
1668
1926
|
|
|
1669
|
-
|
|
1927
|
+
for ( let i = 0, l = lineSegments.length; i < l; i ++ ) {
|
|
1670
1928
|
|
|
1671
|
-
|
|
1929
|
+
const ls = lineSegments[ i ];
|
|
1672
1930
|
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1931
|
+
if ( ls.material === null ) {
|
|
1932
|
+
|
|
1933
|
+
ls.material = parseColorCode( ls.colorCode, true );
|
|
1676
1934
|
|
|
1677
1935
|
}
|
|
1678
1936
|
|
|
1679
1937
|
}
|
|
1680
1938
|
|
|
1681
|
-
|
|
1939
|
+
for ( let i = 0, l = conditionalSegments.length; i < l; i ++ ) {
|
|
1682
1940
|
|
|
1683
|
-
|
|
1941
|
+
const cs = conditionalSegments[ i ];
|
|
1942
|
+
|
|
1943
|
+
if ( cs.material === null ) {
|
|
1944
|
+
|
|
1945
|
+
cs.material = parseColorCode( cs.colorCode, true );
|
|
1946
|
+
|
|
1947
|
+
}
|
|
1684
1948
|
|
|
1685
1949
|
}
|
|
1686
1950
|
|
|
1687
|
-
currentParseScope.
|
|
1688
|
-
currentParseScope.
|
|
1689
|
-
currentParseScope.
|
|
1690
|
-
currentParseScope.
|
|
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;
|
|
1691
1958
|
currentParseScope.subobjectIndex = 0;
|
|
1959
|
+
currentParseScope.type = info.type;
|
|
1960
|
+
currentParseScope.totalFaces = info.totalFaces;
|
|
1692
1961
|
const isRoot = ! parentParseScope.isFromParse;
|
|
1693
1962
|
|
|
1694
|
-
if ( isRoot ||
|
|
1963
|
+
if ( isRoot || ! isPrimitiveType( info.type ) ) {
|
|
1695
1964
|
|
|
1696
1965
|
currentParseScope.groupObject = new THREE.Group();
|
|
1697
1966
|
currentParseScope.groupObject.userData.startingConstructionStep = currentParseScope.startingConstructionStep;
|
|
@@ -1736,17 +2005,21 @@
|
|
|
1736
2005
|
// is added directly into the parent model (meaning it will never get smoothed by
|
|
1737
2006
|
// being added to a part)
|
|
1738
2007
|
|
|
1739
|
-
const doSmooth = isPartType( subobjectParseScope.type ) ||
|
|
2008
|
+
const doSmooth = isPartType( subobjectParseScope.type ) || isPrimitiveType( subobjectParseScope.type ) && isModelType( subobjectParseScope.parentScope.type );
|
|
1740
2009
|
|
|
1741
2010
|
if ( this.smoothNormals && doSmooth ) {
|
|
1742
2011
|
|
|
1743
|
-
|
|
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 );
|
|
1744
2017
|
|
|
1745
2018
|
}
|
|
1746
2019
|
|
|
1747
2020
|
const isRoot = ! parentParseScope.isFromParse;
|
|
1748
2021
|
|
|
1749
|
-
if (
|
|
2022
|
+
if ( ! isPrimitiveType( subobjectParseScope.type ) || isRoot ) {
|
|
1750
2023
|
|
|
1751
2024
|
const objGroup = subobjectParseScope.groupObject;
|
|
1752
2025
|
|
|
@@ -1780,26 +2053,23 @@
|
|
|
1780
2053
|
|
|
1781
2054
|
} else {
|
|
1782
2055
|
|
|
1783
|
-
const separateObjects = this.separateObjects;
|
|
1784
2056
|
const parentLineSegments = parentParseScope.lineSegments;
|
|
1785
2057
|
const parentConditionalSegments = parentParseScope.conditionalSegments;
|
|
1786
2058
|
const parentFaces = parentParseScope.faces;
|
|
2059
|
+
const parentFaceMaterials = parentParseScope.faceMaterials;
|
|
1787
2060
|
const lineSegments = subobjectParseScope.lineSegments;
|
|
1788
2061
|
const conditionalSegments = subobjectParseScope.conditionalSegments;
|
|
1789
2062
|
const faces = subobjectParseScope.faces;
|
|
2063
|
+
const faceMaterials = subobjectParseScope.faceMaterials;
|
|
2064
|
+
const matrix = subobjectParseScope.matrix;
|
|
2065
|
+
const matrixScaleInverted = matrix.determinant() < 0;
|
|
1790
2066
|
|
|
1791
2067
|
for ( let i = 0, l = lineSegments.length; i < l; i ++ ) {
|
|
1792
2068
|
|
|
1793
2069
|
const ls = lineSegments[ i ];
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
const vertices = ls.vertices;
|
|
1798
|
-
vertices[ 0 ].applyMatrix4( subobjectParseScope.matrix );
|
|
1799
|
-
vertices[ 1 ].applyMatrix4( subobjectParseScope.matrix );
|
|
1800
|
-
|
|
1801
|
-
}
|
|
1802
|
-
|
|
2070
|
+
const vertices = ls.vertices;
|
|
2071
|
+
vertices[ 0 ].applyMatrix4( matrix );
|
|
2072
|
+
vertices[ 1 ].applyMatrix4( matrix );
|
|
1803
2073
|
parentLineSegments.push( ls );
|
|
1804
2074
|
|
|
1805
2075
|
}
|
|
@@ -1807,18 +2077,12 @@
|
|
|
1807
2077
|
for ( let i = 0, l = conditionalSegments.length; i < l; i ++ ) {
|
|
1808
2078
|
|
|
1809
2079
|
const os = conditionalSegments[ i ];
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
vertices[ 1 ].applyMatrix4( subobjectParseScope.matrix );
|
|
1817
|
-
controlPoints[ 0 ].applyMatrix4( subobjectParseScope.matrix );
|
|
1818
|
-
controlPoints[ 1 ].applyMatrix4( subobjectParseScope.matrix );
|
|
1819
|
-
|
|
1820
|
-
}
|
|
1821
|
-
|
|
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 );
|
|
1822
2086
|
parentConditionalSegments.push( os );
|
|
1823
2087
|
|
|
1824
2088
|
}
|
|
@@ -1826,22 +2090,19 @@
|
|
|
1826
2090
|
for ( let i = 0, l = faces.length; i < l; i ++ ) {
|
|
1827
2091
|
|
|
1828
2092
|
const tri = faces[ i ];
|
|
2093
|
+
const vertices = tri.vertices;
|
|
1829
2094
|
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
const vertices = tri.vertices;
|
|
2095
|
+
for ( let i = 0, l = vertices.length; i < l; i ++ ) {
|
|
1833
2096
|
|
|
1834
|
-
|
|
2097
|
+
vertices[ i ].applyMatrix4( matrix );
|
|
1835
2098
|
|
|
1836
|
-
|
|
2099
|
+
} // If the scale of the object is negated then the triangle winding order
|
|
2100
|
+
// needs to be flipped.
|
|
1837
2101
|
|
|
1838
|
-
}
|
|
1839
|
-
|
|
1840
|
-
_tempVec0.subVectors( vertices[ 1 ], vertices[ 0 ] );
|
|
1841
2102
|
|
|
1842
|
-
|
|
2103
|
+
if ( matrixScaleInverted !== subobjectParseScope.inverted ) {
|
|
1843
2104
|
|
|
1844
|
-
|
|
2105
|
+
vertices.reverse();
|
|
1845
2106
|
|
|
1846
2107
|
}
|
|
1847
2108
|
|
|
@@ -1850,12 +2111,13 @@
|
|
|
1850
2111
|
}
|
|
1851
2112
|
|
|
1852
2113
|
parentParseScope.totalFaces += subobjectParseScope.totalFaces;
|
|
2114
|
+
faceMaterials.forEach( material => parentFaceMaterials.add( material ) );
|
|
1853
2115
|
|
|
1854
2116
|
}
|
|
1855
2117
|
|
|
1856
2118
|
}
|
|
1857
2119
|
|
|
1858
|
-
async processObject(
|
|
2120
|
+
async processObject( parsedInfo, subobject, url, parentScope ) {
|
|
1859
2121
|
|
|
1860
2122
|
const scope = this;
|
|
1861
2123
|
const parseScope = this.newParseScopeLevel( null, parentScope );
|
|
@@ -1864,18 +2126,28 @@
|
|
|
1864
2126
|
|
|
1865
2127
|
if ( subobject ) {
|
|
1866
2128
|
|
|
1867
|
-
parseScope.currentMatrix.multiplyMatrices( parentParseScope.currentMatrix, subobject.matrix );
|
|
1868
2129
|
parseScope.matrix.copy( subobject.matrix );
|
|
1869
2130
|
parseScope.inverted = subobject.inverted;
|
|
1870
2131
|
parseScope.startingConstructionStep = subobject.startingConstructionStep;
|
|
1871
|
-
parseScope.mainColourCode = subobject.material.userData.code;
|
|
1872
|
-
parseScope.mainEdgeColourCode = subobject.material.userData.edgeMaterial.userData.code;
|
|
1873
2132
|
parseScope.fileName = subobject.fileName;
|
|
1874
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
|
+
|
|
1875
2147
|
} // Parse the object
|
|
1876
2148
|
|
|
1877
2149
|
|
|
1878
|
-
this.objectParse(
|
|
2150
|
+
this.objectParse( parsedInfo, parseScope );
|
|
1879
2151
|
const subobjects = parseScope.subobjects;
|
|
1880
2152
|
const promises = [];
|
|
1881
2153
|
|
|
@@ -1907,13 +2179,13 @@
|
|
|
1907
2179
|
|
|
1908
2180
|
function loadSubobject( subobject ) {
|
|
1909
2181
|
|
|
1910
|
-
return scope.
|
|
2182
|
+
return scope.parseCache.loadData( subobject.fileName ).then( function ( parsedInfo ) {
|
|
1911
2183
|
|
|
1912
|
-
return scope.processObject(
|
|
2184
|
+
return scope.processObject( parsedInfo, subobject, url, parseScope );
|
|
1913
2185
|
|
|
1914
|
-
} ).catch( function () {
|
|
2186
|
+
} ).catch( function ( err ) {
|
|
1915
2187
|
|
|
1916
|
-
console.warn(
|
|
2188
|
+
console.warn( err );
|
|
1917
2189
|
return null;
|
|
1918
2190
|
|
|
1919
2191
|
} );
|