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