super-three 0.133.4 → 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 +2285 -1983
- package/build/three.min.js +1 -1
- package/build/three.module.js +5983 -5358
- package/examples/fonts/open-sans/open-sans-v15-cyrillic-ext_greek_greek-ext_cyrillic_latin_latin-ext_vietnamese-regular.woff +0 -0
- package/examples/fonts/open-sans/open-sans-v15-cyrillic-ext_greek_greek-ext_cyrillic_latin_latin-ext_vietnamese-regular.woff2 +0 -0
- package/examples/fonts/open-sans/open-sans.css +9 -0
- package/examples/fonts/tabler-icons/fonts/tabler-icons.eot +0 -0
- package/examples/fonts/tabler-icons/fonts/tabler-icons.svg +3966 -0
- package/examples/fonts/tabler-icons/fonts/tabler-icons.ttf +0 -0
- package/examples/fonts/tabler-icons/fonts/tabler-icons.woff +0 -0
- package/examples/fonts/tabler-icons/fonts/tabler-icons.woff2 +0 -0
- package/examples/fonts/tabler-icons/tabler-icons.min.css +4 -0
- package/examples/js/animation/CCDIKSolver.js +0 -1
- package/examples/js/animation/MMDPhysics.js +2 -4
- package/examples/js/cameras/CinematicCamera.js +2 -7
- package/examples/js/controls/ArcballControls.js +99 -72
- package/examples/js/controls/DragControls.js +7 -0
- package/examples/js/controls/OrbitControls.js +5 -36
- package/examples/js/controls/TrackballControls.js +3 -2
- package/examples/js/controls/TransformControls.js +24 -1
- package/examples/js/csm/CSM.js +2 -2
- package/examples/js/csm/CSMFrustum.js +133 -0
- package/examples/js/curves/CurveExtras.js +0 -2
- package/examples/js/effects/AsciiEffect.js +2 -9
- package/examples/js/exporters/GLTFExporter.js +148 -64
- package/examples/js/exporters/MMDExporter.js +1 -1
- package/examples/js/exporters/USDZExporter.js +45 -26
- package/examples/js/geometries/ConvexGeometry.js +1 -1
- package/examples/js/geometries/TeapotGeometry.js +0 -1
- package/examples/js/interactive/SelectionBox.js +2 -0
- package/examples/js/interactive/SelectionHelper.js +22 -7
- package/examples/js/lights/RectAreaLightUniformsLib.js +4 -0
- package/examples/js/lines/LineMaterial.js +58 -20
- package/examples/js/lines/LineSegments2.js +29 -24
- package/examples/js/loaders/3DMLoader.js +24 -11
- package/examples/js/loaders/3MFLoader.js +2 -2
- package/examples/js/loaders/AMFLoader.js +0 -2
- package/examples/js/loaders/BasisTextureLoader.js +1 -0
- package/examples/js/loaders/ColladaLoader.js +61 -4
- package/examples/js/loaders/EXRLoader.js +201 -272
- package/examples/js/loaders/FBXLoader.js +4 -5
- package/examples/js/loaders/GLTFLoader.js +98 -35
- package/examples/js/loaders/HDRCubeTextureLoader.js +0 -10
- package/examples/js/loaders/KTX2Loader.js +23 -2
- package/examples/js/loaders/KTXLoader.js +1 -1
- package/examples/js/loaders/LDrawLoader.js +1169 -901
- package/examples/js/loaders/LUT3dlLoader.js +19 -10
- package/examples/js/loaders/LUTCubeLoader.js +5 -4
- package/examples/js/loaders/LWOLoader.js +8 -6
- package/examples/js/loaders/LogLuvLoader.js +766 -0
- package/examples/js/loaders/MMDLoader.js +0 -1
- package/examples/js/loaders/NRRDLoader.js +9 -2
- package/examples/js/loaders/OBJLoader.js +0 -2
- package/examples/js/loaders/PLYLoader.js +33 -8
- package/examples/js/loaders/RGBELoader.js +8 -22
- package/examples/js/loaders/RGBMLoader.js +56 -6
- package/examples/js/loaders/SVGLoader.js +12 -6
- package/examples/js/loaders/TDSLoader.js +237 -271
- package/examples/js/loaders/VOXLoader.js +1 -0
- package/examples/js/loaders/VRMLLoader.js +11 -22
- package/examples/js/loaders/lwo/LWO2Parser.js +1 -0
- package/examples/js/loaders/lwo/LWO3Parser.js +4 -2
- package/examples/js/math/ConvexHull.js +7 -13
- package/examples/js/math/ImprovedNoise.js +1 -1
- package/examples/js/misc/GPUComputationRenderer.js +3 -1
- 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/Lensflare.js +2 -10
- package/examples/js/objects/MarchingCubes.js +42 -128
- package/examples/js/objects/Reflector.js +8 -13
- package/examples/js/objects/ReflectorForSSRPass.js +0 -10
- package/examples/js/objects/Refractor.js +8 -13
- package/examples/js/objects/Sky.js +1 -1
- package/examples/js/objects/Water.js +3 -15
- package/examples/js/objects/Water2.js +4 -7
- package/examples/js/postprocessing/GlitchPass.js +6 -5
- package/examples/js/postprocessing/SMAAPass.js +2 -9
- package/examples/js/postprocessing/SSAOPass.js +10 -21
- package/examples/js/postprocessing/SavePass.js +1 -5
- package/examples/js/renderers/CSS2DRenderer.js +4 -4
- package/examples/js/renderers/CSS3DRenderer.js +4 -4
- package/examples/js/shaders/ConvolutionShader.js +0 -1
- package/examples/js/shaders/DigitalGlitch.js +3 -3
- package/examples/js/shaders/FXAAShader.js +1 -1
- package/examples/js/shaders/FilmShader.js +1 -1
- package/examples/js/shaders/GammaCorrectionShader.js +1 -1
- package/examples/js/shaders/HorizontalBlurShader.js +1 -2
- package/examples/js/shaders/MMDToonShader.js +0 -1
- package/examples/js/shaders/SSAOShader.js +1 -1
- package/examples/js/shaders/SSRShader.js +1 -1
- package/examples/js/shaders/SSRrShader.js +1 -1
- package/examples/js/shaders/VerticalBlurShader.js +1 -2
- package/examples/js/utils/GeometryUtils.js +1 -1
- package/examples/jsm/animation/CCDIKSolver.js +0 -1
- package/examples/jsm/animation/MMDPhysics.js +1 -3
- 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/controls/ArcballControls.js +80 -77
- package/examples/jsm/controls/DragControls.js +7 -0
- package/examples/jsm/controls/OrbitControls.js +9 -51
- package/examples/jsm/controls/TrackballControls.js +1 -1
- package/examples/jsm/controls/TransformControls.js +24 -1
- package/examples/jsm/csm/CSM.js +3 -3
- package/examples/jsm/csm/{Frustum.js → CSMFrustum.js} +3 -3
- package/examples/jsm/curves/CurveExtras.js +0 -2
- package/examples/jsm/effects/AsciiEffect.js +2 -9
- package/examples/jsm/exporters/GLTFExporter.js +169 -99
- package/examples/jsm/exporters/MMDExporter.js +1 -1
- package/examples/jsm/exporters/USDZExporter.js +47 -26
- package/examples/jsm/geometries/ConvexGeometry.js +1 -1
- package/examples/jsm/geometries/TeapotGeometry.js +0 -1
- package/examples/jsm/interactive/SelectionBox.js +1 -0
- package/examples/jsm/interactive/SelectionHelper.js +19 -7
- package/examples/jsm/libs/flow.module.js +3338 -0
- package/examples/jsm/libs/lil-gui.module.min.js +8 -0
- package/examples/jsm/libs/rhino3dm/rhino3dm.wasm +0 -0
- package/examples/jsm/lights/RectAreaLightUniformsLib.js +7 -0
- package/examples/jsm/lines/LineMaterial.js +59 -23
- package/examples/jsm/lines/LineSegments2.js +22 -18
- package/examples/jsm/loaders/3DMLoader.js +18 -12
- package/examples/jsm/loaders/3MFLoader.js +2 -2
- package/examples/jsm/loaders/AMFLoader.js +0 -2
- package/examples/jsm/loaders/BasisTextureLoader.js +7 -0
- package/examples/jsm/loaders/ColladaLoader.js +61 -4
- package/examples/jsm/loaders/EXRLoader.js +200 -306
- package/examples/jsm/loaders/FBXLoader.js +4 -5
- package/examples/jsm/loaders/GLTFLoader.js +102 -40
- package/examples/jsm/loaders/HDRCubeTextureLoader.js +1 -17
- package/examples/jsm/loaders/KTX2Loader.js +30 -2
- package/examples/jsm/loaders/KTXLoader.js +1 -1
- package/examples/jsm/loaders/LDrawLoader.js +1159 -898
- package/examples/jsm/loaders/LUT3dlLoader.js +19 -12
- package/examples/jsm/loaders/LUTCubeLoader.js +5 -5
- package/examples/jsm/loaders/LWOLoader.js +8 -6
- package/examples/jsm/loaders/LogLuvLoader.js +606 -0
- package/examples/jsm/loaders/MMDLoader.js +0 -1
- package/examples/jsm/loaders/NRRDLoader.js +10 -2
- package/examples/jsm/loaders/OBJLoader.js +0 -2
- package/examples/jsm/loaders/PLYLoader.js +34 -8
- package/examples/jsm/loaders/RGBELoader.js +7 -28
- package/examples/jsm/loaders/RGBMLoader.js +62 -8
- package/examples/jsm/loaders/SVGLoader.js +15 -7
- package/examples/jsm/loaders/TDSLoader.js +236 -275
- package/examples/jsm/loaders/VOXLoader.js +1 -0
- package/examples/jsm/loaders/VRMLLoader.js +11 -23
- package/examples/jsm/loaders/lwo/LWO2Parser.js +1 -0
- package/examples/jsm/loaders/lwo/LWO3Parser.js +2 -2
- package/examples/jsm/math/ConvexHull.js +7 -13
- package/examples/jsm/math/ImprovedNoise.js +1 -1
- package/examples/jsm/misc/GPUComputationRenderer.js +3 -1
- 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 +455 -0
- package/examples/jsm/node-editor/accessors/NormalEditor.js +30 -0
- package/examples/jsm/node-editor/accessors/PositionEditor.js +30 -0
- package/examples/jsm/node-editor/accessors/UVEditor.js +26 -0
- package/examples/jsm/node-editor/display/BlendEditor.js +43 -0
- package/examples/jsm/node-editor/examples/animate-uv.json +1 -0
- package/examples/jsm/node-editor/examples/fake-top-light.json +1 -0
- package/examples/jsm/node-editor/examples/oscillator-color.json +1 -0
- package/examples/jsm/node-editor/examples/rim.json +1 -0
- package/examples/jsm/node-editor/inputs/ColorEditor.js +91 -0
- package/examples/jsm/node-editor/inputs/FloatEditor.js +24 -0
- package/examples/jsm/node-editor/inputs/SliderEditor.js +68 -0
- package/examples/jsm/node-editor/inputs/Vector2Editor.js +28 -0
- package/examples/jsm/node-editor/inputs/Vector3Editor.js +30 -0
- package/examples/jsm/node-editor/inputs/Vector4Editor.js +37 -0
- package/examples/jsm/node-editor/materials/StandardMaterialEditor.js +108 -0
- package/examples/jsm/node-editor/math/DotEditor.js +36 -0
- package/examples/jsm/node-editor/math/InvertEditor.js +38 -0
- package/examples/jsm/node-editor/math/LimiterEditor.js +48 -0
- package/examples/jsm/node-editor/math/NormalizeEditor.js +26 -0
- package/examples/jsm/node-editor/math/OperatorEditor.js +50 -0
- package/examples/jsm/node-editor/math/PowerEditor.js +34 -0
- package/examples/jsm/node-editor/math/TrigonometryEditor.js +39 -0
- package/examples/jsm/node-editor/procedural/CheckerEditor.js +26 -0
- package/examples/jsm/node-editor/utils/OscillatorEditor.js +42 -0
- package/examples/jsm/node-editor/utils/TimerEditor.js +57 -0
- package/examples/jsm/nodes/core/NodeBuilder.js +2 -2
- package/examples/jsm/nodes/materials/StandardNodeMaterial.js +1 -1
- package/examples/jsm/nodes/materials/nodes/StandardNode.js +7 -6
- package/examples/jsm/nodes/math/MathNode.js +12 -0
- package/examples/jsm/nodes/misc/BumpMapNode.js +1 -1
- package/examples/jsm/nodes/misc/TextureCubeUVNode.js +1 -4
- package/examples/jsm/nodes/utils/ColorSpaceNode.js +5 -177
- package/examples/jsm/objects/Lensflare.js +5 -16
- package/examples/jsm/objects/MarchingCubes.js +46 -152
- package/examples/jsm/objects/Reflector.js +8 -16
- package/examples/jsm/objects/ReflectorForSSRPass.js +0 -12
- package/examples/jsm/objects/Refractor.js +8 -16
- package/examples/jsm/objects/Sky.js +1 -1
- package/examples/jsm/objects/Water.js +3 -18
- package/examples/jsm/objects/Water2.js +4 -8
- package/examples/jsm/postprocessing/AdaptiveToneMappingPass.js +1 -1
- package/examples/jsm/postprocessing/AfterimagePass.js +1 -1
- package/examples/jsm/postprocessing/BloomPass.js +1 -1
- package/examples/jsm/postprocessing/BokehPass.js +1 -1
- package/examples/jsm/postprocessing/ClearPass.js +1 -1
- package/examples/jsm/postprocessing/CubeTexturePass.js +1 -1
- package/examples/jsm/postprocessing/DotScreenPass.js +1 -1
- package/examples/jsm/postprocessing/EffectComposer.js +3 -3
- package/examples/jsm/postprocessing/FilmPass.js +1 -1
- package/examples/jsm/postprocessing/GlitchPass.js +10 -7
- package/examples/jsm/postprocessing/HalftonePass.js +1 -1
- package/examples/jsm/postprocessing/MaskPass.js +1 -1
- package/examples/jsm/postprocessing/OutlinePass.js +1 -1
- package/examples/jsm/postprocessing/RenderPass.js +1 -1
- package/examples/jsm/postprocessing/SAOPass.js +2 -2
- package/examples/jsm/postprocessing/SMAAPass.js +3 -12
- package/examples/jsm/postprocessing/SSAARenderPass.js +1 -1
- package/examples/jsm/postprocessing/SSAOPass.js +15 -26
- package/examples/jsm/postprocessing/SSRPass.js +1 -1
- package/examples/jsm/postprocessing/SSRrPass.js +1 -1
- package/examples/jsm/postprocessing/SavePass.js +2 -4
- package/examples/jsm/postprocessing/ShaderPass.js +1 -1
- package/examples/jsm/postprocessing/TAARenderPass.js +1 -1
- package/examples/jsm/postprocessing/TexturePass.js +1 -1
- package/examples/jsm/postprocessing/UnrealBloomPass.js +1 -1
- package/examples/jsm/renderers/CSS2DRenderer.js +5 -4
- package/examples/jsm/renderers/CSS3DRenderer.js +5 -4
- package/examples/jsm/renderers/nodes/Nodes.js +17 -13
- package/examples/jsm/renderers/nodes/ShaderNode.js +229 -49
- package/examples/jsm/renderers/nodes/accessors/MaterialNode.js +6 -6
- package/examples/jsm/renderers/nodes/accessors/ModelViewProjectionNode.js +5 -5
- package/examples/jsm/renderers/nodes/accessors/NormalNode.js +14 -3
- package/examples/jsm/renderers/nodes/accessors/PointUVNode.js +3 -3
- package/examples/jsm/renderers/nodes/accessors/PositionNode.js +15 -5
- package/examples/jsm/renderers/nodes/accessors/SkinningNode.js +107 -0
- package/examples/jsm/renderers/nodes/accessors/UVNode.js +7 -5
- package/examples/jsm/renderers/nodes/core/ArrayInputNode.js +3 -3
- package/examples/jsm/renderers/nodes/core/AttributeNode.js +6 -0
- package/examples/jsm/renderers/nodes/core/BypassNode.js +38 -0
- package/examples/jsm/renderers/nodes/core/CodeNode.js +3 -3
- package/examples/jsm/renderers/nodes/core/ContextNode.js +4 -19
- package/examples/jsm/renderers/nodes/core/ExpressionNode.js +13 -2
- package/examples/jsm/renderers/nodes/core/FunctionNode.js +19 -132
- package/examples/jsm/renderers/nodes/core/InputNode.js +13 -3
- package/examples/jsm/renderers/nodes/core/Node.js +32 -10
- package/examples/jsm/renderers/nodes/core/NodeBuilder.js +146 -128
- package/examples/jsm/renderers/nodes/core/NodeFunction.js +22 -0
- package/examples/jsm/renderers/nodes/core/NodeFunctionInput.js +4 -4
- package/examples/jsm/renderers/nodes/core/NodeKeywords.js +18 -179
- package/examples/jsm/renderers/nodes/core/NodeParser.js +11 -0
- package/examples/jsm/renderers/nodes/core/NodeVar.js +2 -2
- package/examples/jsm/renderers/nodes/core/PropertyNode.js +14 -2
- package/examples/jsm/renderers/nodes/core/TempNode.js +10 -13
- package/examples/jsm/renderers/nodes/core/VarNode.js +12 -7
- package/examples/jsm/renderers/nodes/core/VaryNode.js +23 -8
- package/examples/jsm/renderers/nodes/core/constants.js +2 -0
- package/examples/jsm/renderers/nodes/display/ColorSpaceNode.js +43 -63
- package/examples/jsm/renderers/nodes/display/NormalMapNode.js +21 -22
- package/examples/jsm/renderers/nodes/functions/BSDFs.js +64 -101
- package/examples/jsm/renderers/nodes/functions/PhysicalMaterialFunctions.js +27 -0
- package/examples/jsm/renderers/nodes/inputs/BufferNode.js +25 -0
- package/examples/jsm/renderers/nodes/inputs/IntNode.js +17 -0
- package/examples/jsm/renderers/nodes/inputs/TextureNode.js +13 -10
- package/examples/jsm/renderers/nodes/lights/LightContextNode.js +20 -14
- package/examples/jsm/renderers/nodes/lights/LightNode.js +32 -36
- package/examples/jsm/renderers/nodes/math/CondNode.js +60 -0
- package/examples/jsm/renderers/nodes/math/MathNode.js +91 -37
- package/examples/jsm/renderers/nodes/math/OperatorNode.js +124 -31
- package/examples/jsm/renderers/nodes/parsers/GLSLNodeFunction.js +137 -0
- package/examples/jsm/renderers/nodes/parsers/GLSLNodeParser.js +14 -0
- package/examples/jsm/renderers/nodes/parsers/WGSLNodeFunction.js +89 -0
- package/examples/jsm/renderers/nodes/parsers/WGSLNodeParser.js +14 -0
- package/examples/jsm/renderers/nodes/procedural/CheckerNode.js +7 -9
- package/examples/jsm/renderers/nodes/utils/ArrayElementNode.js +31 -0
- package/examples/jsm/renderers/nodes/utils/ConvertNode.js +33 -0
- package/examples/jsm/renderers/nodes/utils/JoinNode.js +7 -7
- package/examples/jsm/renderers/nodes/utils/OscNode.js +58 -0
- package/examples/jsm/renderers/nodes/utils/SplitNode.js +18 -8
- package/examples/jsm/renderers/nodes/utils/SpriteSheetUVNode.js +7 -7
- package/examples/jsm/renderers/nodes/utils/TimerNode.js +1 -1
- package/examples/jsm/renderers/webgl/nodes/SlotNode.js +22 -0
- package/examples/jsm/renderers/webgl/nodes/WebGLNodeBuilder.js +192 -95
- package/examples/jsm/renderers/webgl/nodes/WebGLNodes.js +2 -2
- package/examples/jsm/renderers/webgl/nodes/WebGLPhysicalContextNode.js +2 -2
- package/examples/jsm/renderers/webgpu/WebGPUBackground.js +1 -1
- package/examples/jsm/renderers/webgpu/WebGPUBindings.js +10 -9
- package/examples/jsm/renderers/webgpu/WebGPUComputePipelines.js +2 -4
- package/examples/jsm/renderers/webgpu/WebGPUObjects.js +1 -1
- package/examples/jsm/renderers/webgpu/WebGPUProgrammableStage.js +2 -4
- package/examples/jsm/renderers/webgpu/WebGPURenderPipeline.js +15 -23
- package/examples/jsm/renderers/webgpu/WebGPURenderPipelines.js +44 -65
- package/examples/jsm/renderers/webgpu/WebGPURenderer.js +13 -31
- package/examples/jsm/renderers/webgpu/WebGPUTextureUtils.js +61 -28
- package/examples/jsm/renderers/webgpu/WebGPUTextures.js +35 -29
- package/examples/jsm/renderers/webgpu/constants.js +2 -2
- package/examples/jsm/renderers/webgpu/nodes/WebGPUNodeBuilder.js +459 -95
- package/examples/jsm/renderers/webgpu/nodes/WebGPUNodes.js +1 -1
- package/examples/jsm/shaders/ConvolutionShader.js +0 -1
- package/examples/jsm/shaders/DigitalGlitch.js +3 -3
- package/examples/jsm/shaders/FXAAShader.js +1 -1
- package/examples/jsm/shaders/FilmShader.js +1 -1
- package/examples/jsm/shaders/GammaCorrectionShader.js +1 -1
- package/examples/jsm/shaders/HorizontalBlurShader.js +1 -2
- package/examples/jsm/shaders/MMDToonShader.js +0 -1
- package/examples/jsm/shaders/SSAOShader.js +1 -1
- package/examples/jsm/shaders/SSRShader.js +1 -1
- package/examples/jsm/shaders/SSRrShader.js +1 -1
- package/examples/jsm/shaders/VerticalBlurShader.js +1 -2
- package/examples/jsm/utils/GeometryUtils.js +1 -1
- package/examples/jsm/utils/LDrawUtils.js +195 -0
- package/examples/jsm/utils/WorkerPool.js +11 -11
- package/examples/jsm/webxr/OculusHandPointerModel.js +6 -6
- package/examples/jsm/webxr/VRButton.js +24 -0
- package/examples/jsm/webxr/XREstimatedLight.js +2 -0
- package/package.json +27 -18
- package/src/Three.Legacy.js +18 -0
- package/src/Three.js +1 -3
- package/src/animation/AnimationMixer.js +1 -0
- package/src/cameras/CubeCamera.js +2 -0
- package/src/cameras/StereoCamera.js +8 -7
- package/src/constants.js +3 -23
- package/src/core/Layers.js +7 -1
- package/src/core/Object3D.js +3 -1
- package/src/extras/ImageUtils.js +63 -0
- package/src/extras/PMREMGenerator.js +49 -185
- package/src/extras/core/Interpolations.js +1 -1
- package/src/geometries/LatheGeometry.js +70 -47
- package/src/helpers/CameraHelper.js +1 -1
- package/src/loaders/FileLoader.js +98 -144
- package/src/loaders/ImageLoader.js +9 -4
- package/src/loaders/LoaderUtils.js +26 -0
- package/src/loaders/MaterialLoader.js +8 -5
- package/src/loaders/ObjectLoader.js +2 -0
- package/src/materials/Material.js +8 -8
- package/src/materials/MeshPhysicalMaterial.js +20 -14
- package/src/math/Color.js +1 -39
- package/src/math/MathUtils.js +5 -16
- package/src/math/Quaternion.js +1 -1
- package/src/math/Ray.js +2 -2
- package/src/math/Sphere.js +10 -1
- package/src/objects/Skeleton.js +1 -0
- package/src/renderers/WebGLCubeRenderTarget.js +0 -2
- package/src/renderers/WebGLMultisampleRenderTarget.js +7 -1
- package/src/renderers/WebGLRenderer.js +106 -199
- package/src/renderers/shaders/ShaderChunk/bsdfs.glsl.js +2 -2
- package/src/renderers/shaders/ShaderChunk/bumpmap_pars_fragment.glsl.js +1 -1
- package/src/renderers/shaders/ShaderChunk/cube_uv_reflection_fragment.glsl.js +2 -24
- package/src/renderers/shaders/ShaderChunk/emissivemap_fragment.glsl.js +0 -2
- package/src/renderers/shaders/ShaderChunk/encodings_pars_fragment.glsl.js +0 -78
- package/src/renderers/shaders/ShaderChunk/envmap_common_pars_fragment.glsl.js +0 -1
- package/src/renderers/shaders/ShaderChunk/envmap_fragment.glsl.js +0 -2
- package/src/renderers/shaders/ShaderChunk/gradientmap_pars_fragment.glsl.js +1 -1
- 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 +19 -6
- package/src/renderers/shaders/ShaderChunk/lights_physical_pars_fragment.glsl.js +28 -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/tonemapping_pars_fragment.glsl.js +1 -1
- package/src/renderers/shaders/ShaderChunk/transmission_fragment.glsl.js +1 -1
- package/src/renderers/shaders/ShaderChunk/transmission_pars_fragment.glsl.js +9 -8
- 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/meshphysical.glsl.js +23 -5
- package/src/renderers/shaders/ShaderLib/vsm.glsl.js +2 -1
- package/src/renderers/shaders/ShaderLib.js +8 -6
- package/src/renderers/shaders/UniformsLib.js +1 -2
- package/src/renderers/webgl/WebGLCapabilities.js +0 -2
- package/src/renderers/webgl/WebGLCubeMaps.js +0 -4
- package/src/renderers/webgl/WebGLCubeUVMaps.js +29 -16
- package/src/renderers/webgl/WebGLExtensions.js +1 -0
- package/src/renderers/webgl/WebGLMaterials.js +27 -15
- package/src/renderers/webgl/WebGLMorphtargets.js +13 -0
- package/src/renderers/webgl/WebGLProgram.js +12 -37
- package/src/renderers/webgl/WebGLPrograms.js +192 -70
- package/src/renderers/webgl/WebGLRenderLists.js +4 -14
- package/src/renderers/webgl/WebGLShaderCache.js +120 -0
- package/src/renderers/webgl/WebGLShadowMap.js +14 -5
- package/src/renderers/webgl/WebGLState.js +157 -17
- package/src/renderers/webgl/WebGLTextures.js +442 -83
- package/src/renderers/webgl/WebGLUniforms.js +45 -1
- package/src/renderers/webgl/WebGLUtils.js +86 -28
- package/src/renderers/webxr/WebXRManager.js +77 -109
- package/src/scenes/Scene.js +1 -1
- package/src/textures/DataTexture.js +0 -2
- package/src/textures/DataTexture2DArray.js +0 -2
- package/src/textures/DataTexture3D.js +0 -2
- package/src/textures/FramebufferTexture.js +25 -0
- package/src/textures/Texture.js +8 -1
- package/src/textures/VideoTexture.js +1 -3
- package/examples/js/WebGL.js +0 -90
- package/examples/js/controls/DeviceOrientationControls.js +0 -147
- package/examples/js/libs/dat.gui.min.js +0 -14
- package/examples/js/utils/RoughnessMipmapper.js +0 -278
- package/examples/jsm/controls/DeviceOrientationControls.js +0 -153
- package/examples/jsm/libs/dat.gui.module.js +0 -3575
- package/examples/jsm/libs/glslang.js +0 -78
- package/examples/jsm/libs/glslang.wasm +0 -0
- package/examples/jsm/renderers/nodes/consts/MathConsts.js +0 -7
- package/examples/jsm/renderers/nodes/core/ConstNode.js +0 -39
- package/examples/jsm/renderers/nodes/core/NodeSlot.js +0 -13
- package/examples/jsm/renderers/nodes/core/StructNode.js +0 -80
- package/examples/jsm/renderers/nodes/core/StructVarNode.js +0 -75
- package/examples/jsm/renderers/nodes/functions/EncodingFunctions.js +0 -99
- package/examples/jsm/renderers/nodes/functions/MathFunctions.js +0 -47
- package/examples/jsm/renderers/webgpu/nodes/ShaderLib.js +0 -152
- package/examples/jsm/utils/RoughnessMipmapper.js +0 -300
- package/src/extras/objects/ImmediateRenderObject.js +0 -31
|
@@ -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,253 +599,750 @@
|
|
|
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 ) {
|
|
876
|
+
|
|
877
|
+
// Start embedded text files parsing
|
|
878
|
+
parsingEmbeddedFiles = true;
|
|
879
|
+
currentEmbeddedFileName = lp.getRemainingString();
|
|
880
|
+
currentEmbeddedText = '';
|
|
881
|
+
bfcCertified = false;
|
|
882
|
+
bfcCCW = true;
|
|
883
|
+
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
break;
|
|
887
|
+
|
|
888
|
+
case 'BFC':
|
|
889
|
+
// Changes to the backface culling state
|
|
890
|
+
while ( ! lp.isAtTheEnd() ) {
|
|
891
|
+
|
|
892
|
+
const token = lp.getToken();
|
|
893
|
+
|
|
894
|
+
switch ( token ) {
|
|
895
|
+
|
|
896
|
+
case 'CERTIFY':
|
|
897
|
+
case 'NOCERTIFY':
|
|
898
|
+
bfcCertified = token === 'CERTIFY';
|
|
899
|
+
bfcCCW = true;
|
|
900
|
+
break;
|
|
901
|
+
|
|
902
|
+
case 'CW':
|
|
903
|
+
case 'CCW':
|
|
904
|
+
bfcCCW = token === 'CCW';
|
|
905
|
+
break;
|
|
906
|
+
|
|
907
|
+
case 'INVERTNEXT':
|
|
908
|
+
bfcInverted = true;
|
|
909
|
+
break;
|
|
910
|
+
|
|
911
|
+
case 'CLIP':
|
|
912
|
+
case 'NOCLIP':
|
|
913
|
+
bfcCull = token === 'CLIP';
|
|
914
|
+
break;
|
|
915
|
+
|
|
916
|
+
default:
|
|
917
|
+
console.warn( 'THREE.LDrawLoader: BFC directive "' + token + '" is unknown.' );
|
|
918
|
+
break;
|
|
919
|
+
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
break;
|
|
925
|
+
|
|
926
|
+
case 'STEP':
|
|
927
|
+
startingConstructionStep = true;
|
|
928
|
+
break;
|
|
929
|
+
|
|
930
|
+
default:
|
|
931
|
+
// Other meta directives are not implemented
|
|
932
|
+
break;
|
|
933
|
+
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
break;
|
|
939
|
+
// Line type 1: Sub-object file
|
|
940
|
+
|
|
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, '/' );
|
|
958
|
+
|
|
959
|
+
if ( loader.fileMap[ fileName ] ) {
|
|
960
|
+
|
|
961
|
+
// Found the subobject path in the preloaded file path map
|
|
962
|
+
fileName = loader.fileMap[ fileName ];
|
|
963
|
+
|
|
964
|
+
} else {
|
|
965
|
+
|
|
966
|
+
// Standardized subfolders
|
|
967
|
+
if ( fileName.startsWith( 's/' ) ) {
|
|
968
|
+
|
|
969
|
+
fileName = 'parts/' + fileName;
|
|
970
|
+
|
|
971
|
+
} else if ( fileName.startsWith( '48/' ) ) {
|
|
972
|
+
|
|
973
|
+
fileName = 'p/' + fileName;
|
|
974
|
+
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
}
|
|
978
|
+
|
|
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
|
|
990
|
+
|
|
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
|
|
1004
|
+
|
|
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
|
|
1021
|
+
|
|
1022
|
+
case '3':
|
|
1023
|
+
colorCode = lp.getToken();
|
|
1024
|
+
material = getLocalMaterial( colorCode );
|
|
1025
|
+
ccw = bfcCCW;
|
|
1026
|
+
doubleSided = ! bfcCertified || ! bfcCull;
|
|
1027
|
+
|
|
1028
|
+
if ( ccw === true ) {
|
|
1029
|
+
|
|
1030
|
+
v0 = lp.getVector();
|
|
1031
|
+
v1 = lp.getVector();
|
|
1032
|
+
v2 = lp.getVector();
|
|
1033
|
+
|
|
1034
|
+
} else {
|
|
1035
|
+
|
|
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() + '.' );
|
|
1117
|
+
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1120
|
+
}
|
|
1121
|
+
|
|
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
|
+
};
|
|
1140
|
+
|
|
1141
|
+
}
|
|
1142
|
+
|
|
1143
|
+
loadData( fileName ) {
|
|
1144
|
+
|
|
1145
|
+
const key = fileName.toLowerCase();
|
|
1146
|
+
|
|
1147
|
+
if ( ! ( key in this.cache ) ) {
|
|
1148
|
+
|
|
1149
|
+
this.cache[ key ] = this.fetchData( fileName ).then( text => {
|
|
1150
|
+
|
|
1151
|
+
return this.parse( text );
|
|
1152
|
+
|
|
1153
|
+
} );
|
|
1154
|
+
|
|
1155
|
+
}
|
|
1156
|
+
|
|
1157
|
+
return this.cache[ key ].then( result => {
|
|
1158
|
+
|
|
1159
|
+
return this.cloneResult( result );
|
|
1160
|
+
|
|
1161
|
+
} );
|
|
1162
|
+
|
|
1163
|
+
}
|
|
1164
|
+
|
|
1165
|
+
setData( fileName, text ) {
|
|
1166
|
+
|
|
1167
|
+
const key = fileName.toLowerCase();
|
|
1168
|
+
this.cache[ key ] = Promise.resolve( this.parse( text ) );
|
|
1169
|
+
|
|
1170
|
+
}
|
|
1171
|
+
|
|
1172
|
+
}
|
|
1173
|
+
|
|
1174
|
+
function sortByMaterial( a, b ) {
|
|
1175
|
+
|
|
1176
|
+
if ( a.colorCode === b.colorCode ) {
|
|
1177
|
+
|
|
1178
|
+
return 0;
|
|
1179
|
+
|
|
1180
|
+
}
|
|
1181
|
+
|
|
1182
|
+
if ( a.colorCode < b.colorCode ) {
|
|
1183
|
+
|
|
1184
|
+
return - 1;
|
|
1185
|
+
|
|
1186
|
+
}
|
|
1187
|
+
|
|
1188
|
+
return 1;
|
|
1189
|
+
|
|
1190
|
+
}
|
|
1191
|
+
|
|
1192
|
+
function createObject( elements, elementSize, isConditionalSegments = false, totalElements = null ) {
|
|
1193
|
+
|
|
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 );
|
|
1198
|
+
|
|
1199
|
+
if ( totalElements === null ) {
|
|
1200
|
+
|
|
1201
|
+
totalElements = elements.length;
|
|
1202
|
+
|
|
1203
|
+
}
|
|
1204
|
+
|
|
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;
|
|
1214
|
+
|
|
1215
|
+
for ( let iElem = 0, nElem = elements.length; iElem < nElem; iElem ++ ) {
|
|
1216
|
+
|
|
1217
|
+
const elem = elements[ iElem ];
|
|
1218
|
+
let vertices = elem.vertices;
|
|
1219
|
+
|
|
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;
|
|
1229
|
+
|
|
1230
|
+
}
|
|
1231
|
+
|
|
1232
|
+
for ( let j = 0, l = vertices.length; j < l; j ++ ) {
|
|
1233
|
+
|
|
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;
|
|
1239
|
+
|
|
1240
|
+
} // create the normals array if this is a set of faces
|
|
1241
|
+
|
|
1242
|
+
|
|
1243
|
+
if ( elementSize === 3 ) {
|
|
1244
|
+
|
|
1245
|
+
if ( ! elem.faceNormal ) {
|
|
1246
|
+
|
|
1247
|
+
const v0 = vertices[ 0 ];
|
|
1248
|
+
const v1 = vertices[ 1 ];
|
|
1249
|
+
const v2 = vertices[ 2 ];
|
|
1250
|
+
|
|
1251
|
+
_tempVec0.subVectors( v1, v0 );
|
|
1252
|
+
|
|
1253
|
+
_tempVec1.subVectors( v2, v1 );
|
|
1254
|
+
|
|
1255
|
+
elem.faceNormal = new THREE.Vector3().crossVectors( _tempVec0, _tempVec1 ).normalize();
|
|
1256
|
+
|
|
1257
|
+
}
|
|
1258
|
+
|
|
1259
|
+
let elemNormals = elem.normals;
|
|
1260
|
+
|
|
1261
|
+
if ( elemNormals.length === 4 ) {
|
|
1262
|
+
|
|
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;
|
|
1270
|
+
|
|
1271
|
+
}
|
|
1272
|
+
|
|
1273
|
+
for ( let j = 0, l = elemNormals.length; j < l; j ++ ) {
|
|
1274
|
+
|
|
1275
|
+
// use face normal if a vertex normal is not provided
|
|
1276
|
+
let n = elem.faceNormal;
|
|
1277
|
+
|
|
1278
|
+
if ( elemNormals[ j ] ) {
|
|
1279
|
+
|
|
1280
|
+
n = elemNormals[ j ].norm;
|
|
1281
|
+
|
|
1282
|
+
}
|
|
1283
|
+
|
|
1284
|
+
const index = offset + j * 3;
|
|
1285
|
+
normals[ index + 0 ] = n.x;
|
|
1286
|
+
normals[ index + 1 ] = n.y;
|
|
1287
|
+
normals[ index + 2 ] = n.z;
|
|
1288
|
+
|
|
1289
|
+
}
|
|
1290
|
+
|
|
1291
|
+
}
|
|
1292
|
+
|
|
1293
|
+
if ( prevMaterial !== elem.material ) {
|
|
1294
|
+
|
|
1295
|
+
if ( prevMaterial !== null ) {
|
|
1296
|
+
|
|
1297
|
+
bufferGeometry.addGroup( index0, numGroupVerts, materials.length - 1 );
|
|
1298
|
+
|
|
1299
|
+
}
|
|
1300
|
+
|
|
1301
|
+
const material = elem.material;
|
|
1302
|
+
|
|
1303
|
+
if ( elementSize === 3 ) {
|
|
1304
|
+
|
|
1305
|
+
materials.push( material );
|
|
1306
|
+
|
|
1307
|
+
} else if ( elementSize === 2 ) {
|
|
1308
|
+
|
|
1309
|
+
if ( isConditionalSegments ) {
|
|
1310
|
+
|
|
1311
|
+
materials.push( material.userData.edgeMaterial.userData.conditionalEdgeMaterial );
|
|
1312
|
+
|
|
1313
|
+
} else {
|
|
1314
|
+
|
|
1315
|
+
materials.push( material.userData.edgeMaterial );
|
|
1316
|
+
|
|
1317
|
+
}
|
|
1318
|
+
|
|
1319
|
+
}
|
|
1320
|
+
|
|
1321
|
+
prevMaterial = elem.material;
|
|
1322
|
+
index0 = offset / 3;
|
|
1323
|
+
numGroupVerts = vertices.length;
|
|
1324
|
+
|
|
1325
|
+
} else {
|
|
1326
|
+
|
|
1327
|
+
numGroupVerts += vertices.length;
|
|
1328
|
+
|
|
1329
|
+
}
|
|
1330
|
+
|
|
1331
|
+
offset += 3 * vertices.length;
|
|
1332
|
+
|
|
1333
|
+
}
|
|
1334
|
+
|
|
1335
|
+
if ( numGroupVerts > 0 ) {
|
|
1336
|
+
|
|
1337
|
+
bufferGeometry.addGroup( index0, Infinity, materials.length - 1 );
|
|
1338
|
+
|
|
1339
|
+
}
|
|
1340
|
+
|
|
1341
|
+
bufferGeometry.setAttribute( 'position', new THREE.BufferAttribute( positions, 3 ) );
|
|
1342
|
+
|
|
1343
|
+
if ( normals !== null ) {
|
|
1344
|
+
|
|
1345
|
+
bufferGeometry.setAttribute( 'normal', new THREE.BufferAttribute( normals, 3 ) );
|
|
709
1346
|
|
|
710
1347
|
}
|
|
711
1348
|
|
|
@@ -779,15 +1416,12 @@
|
|
|
779
1416
|
this.materials = []; // Not using THREE.Cache here because it returns the previous HTML error response instead of calling onError()
|
|
780
1417
|
// This also allows to handle the embedded text files ("0 FILE" lines)
|
|
781
1418
|
|
|
782
|
-
this.
|
|
783
|
-
|
|
784
|
-
this.fileMap = null;
|
|
785
|
-
this.rootParseScope = this.newParseScopeLevel(); // Add default main triangle and line edge materials (used in pieces that can be coloured with a main color)
|
|
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.
|
|
786
1420
|
|
|
787
|
-
this.
|
|
788
|
-
|
|
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)
|
|
789
1423
|
|
|
790
|
-
this.
|
|
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.
|
|
791
1425
|
|
|
792
1426
|
this.smoothNormals = true; // The path to load parts from the LDraw parts library from.
|
|
793
1427
|
|
|
@@ -820,7 +1454,7 @@
|
|
|
820
1454
|
if ( colorLineRegex.test( line ) ) {
|
|
821
1455
|
|
|
822
1456
|
const directive = line.replace( colorLineRegex, '' );
|
|
823
|
-
const material = this.
|
|
1457
|
+
const material = this.parseColorMetaDirective( new LineParser( directive ) );
|
|
824
1458
|
materials.push( material );
|
|
825
1459
|
|
|
826
1460
|
}
|
|
@@ -833,19 +1467,14 @@
|
|
|
833
1467
|
|
|
834
1468
|
load( url, onLoad, onProgress, onError ) {
|
|
835
1469
|
|
|
836
|
-
if ( ! this.fileMap ) {
|
|
837
|
-
|
|
838
|
-
this.fileMap = {};
|
|
839
|
-
|
|
840
|
-
}
|
|
841
|
-
|
|
842
1470
|
const fileLoader = new THREE.FileLoader( this.manager );
|
|
843
1471
|
fileLoader.setPath( this.path );
|
|
844
1472
|
fileLoader.setRequestHeader( this.requestHeader );
|
|
845
1473
|
fileLoader.setWithCredentials( this.withCredentials );
|
|
846
1474
|
fileLoader.load( url, text => {
|
|
847
1475
|
|
|
848
|
-
|
|
1476
|
+
const parsedInfo = this.parseCache.parse( text );
|
|
1477
|
+
this.processObject( parsedInfo, null, url, this.rootParseScope ).then( function ( result ) {
|
|
849
1478
|
|
|
850
1479
|
onLoad( result.groupObject );
|
|
851
1480
|
|
|
@@ -857,8 +1486,9 @@
|
|
|
857
1486
|
|
|
858
1487
|
parse( text, path, onLoad ) {
|
|
859
1488
|
|
|
860
|
-
// Async parse.
|
|
861
|
-
|
|
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 ) {
|
|
862
1492
|
|
|
863
1493
|
onLoad( result.groupObject );
|
|
864
1494
|
|
|
@@ -866,849 +1496,476 @@
|
|
|
866
1496
|
|
|
867
1497
|
}
|
|
868
1498
|
|
|
869
|
-
setMaterials( materials ) {
|
|
870
|
-
|
|
871
|
-
// Clears parse scopes stack, adds new scope with material library
|
|
872
|
-
this.rootParseScope = this.newParseScopeLevel( materials );
|
|
873
|
-
this.rootParseScope.isFromParse = false;
|
|
874
|
-
this.materials = materials;
|
|
875
|
-
return this;
|
|
876
|
-
|
|
877
|
-
}
|
|
878
|
-
|
|
879
|
-
setFileMap( fileMap ) {
|
|
880
|
-
|
|
881
|
-
this.fileMap = fileMap;
|
|
882
|
-
return this;
|
|
883
|
-
|
|
884
|
-
}
|
|
885
|
-
|
|
886
|
-
newParseScopeLevel( materials = null, parentScope = null ) {
|
|
887
|
-
|
|
888
|
-
// Adds a new scope level, assign materials to it and returns it
|
|
889
|
-
const matLib = {};
|
|
890
|
-
|
|
891
|
-
if ( materials ) {
|
|
892
|
-
|
|
893
|
-
for ( let i = 0, n = materials.length; i < n; i ++ ) {
|
|
894
|
-
|
|
895
|
-
const material = materials[ i ];
|
|
896
|
-
matLib[ material.userData.code ] = material;
|
|
897
|
-
|
|
898
|
-
}
|
|
899
|
-
|
|
900
|
-
}
|
|
901
|
-
|
|
902
|
-
const newParseScope = {
|
|
903
|
-
parentScope: parentScope,
|
|
904
|
-
lib: matLib,
|
|
905
|
-
url: null,
|
|
906
|
-
// Subobjects
|
|
907
|
-
subobjects: null,
|
|
908
|
-
numSubobjects: 0,
|
|
909
|
-
subobjectIndex: 0,
|
|
910
|
-
inverted: false,
|
|
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
|
-
// If false, it is a root material scope previous to parse
|
|
920
|
-
isFromParse: true,
|
|
921
|
-
faces: null,
|
|
922
|
-
lineSegments: null,
|
|
923
|
-
conditionalSegments: null,
|
|
924
|
-
totalFaces: 0,
|
|
925
|
-
// If true, this object is the start of a construction step
|
|
926
|
-
startingConstructionStep: false
|
|
927
|
-
};
|
|
928
|
-
return newParseScope;
|
|
929
|
-
|
|
930
|
-
}
|
|
931
|
-
|
|
932
|
-
addMaterial( material, parseScope ) {
|
|
933
|
-
|
|
934
|
-
// Adds a material to the material library which is on top of the parse scopes stack. And also to the materials array
|
|
935
|
-
const matLib = parseScope.lib;
|
|
936
|
-
|
|
937
|
-
if ( ! matLib[ material.userData.code ] ) {
|
|
938
|
-
|
|
939
|
-
this.materials.push( material );
|
|
940
|
-
|
|
941
|
-
}
|
|
942
|
-
|
|
943
|
-
matLib[ material.userData.code ] = material;
|
|
944
|
-
return this;
|
|
945
|
-
|
|
946
|
-
}
|
|
947
|
-
|
|
948
|
-
getMaterial( colourCode, parseScope = this.rootParseScope ) {
|
|
949
|
-
|
|
950
|
-
// Given a colour code search its material in the parse scopes stack
|
|
951
|
-
if ( colourCode.startsWith( '0x2' ) ) {
|
|
952
|
-
|
|
953
|
-
// Special 'direct' material value (RGB colour)
|
|
954
|
-
const colour = colourCode.substring( 3 );
|
|
955
|
-
return this.parseColourMetaDirective( new LineParser( 'Direct_Color_' + colour + ' CODE -1 VALUE #' + colour + ' EDGE #' + colour + '' ) );
|
|
956
|
-
|
|
957
|
-
}
|
|
958
|
-
|
|
959
|
-
while ( parseScope ) {
|
|
960
|
-
|
|
961
|
-
const material = parseScope.lib[ colourCode ];
|
|
962
|
-
|
|
963
|
-
if ( material ) {
|
|
964
|
-
|
|
965
|
-
return material;
|
|
966
|
-
|
|
967
|
-
} else {
|
|
968
|
-
|
|
969
|
-
parseScope = parseScope.parentScope;
|
|
970
|
-
|
|
971
|
-
}
|
|
972
|
-
|
|
973
|
-
} // Material was not found
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
return null;
|
|
977
|
-
|
|
978
|
-
}
|
|
979
|
-
|
|
980
|
-
parseColourMetaDirective( lineParser ) {
|
|
981
|
-
|
|
982
|
-
// Parses a colour definition and returns a THREE.Material
|
|
983
|
-
let code = null; // Triangle and line colours
|
|
984
|
-
|
|
985
|
-
let colour = 0xFF00FF;
|
|
986
|
-
let edgeColour = 0xFF00FF; // Transparency
|
|
987
|
-
|
|
988
|
-
let alpha = 1;
|
|
989
|
-
let isTransparent = false; // Self-illumination:
|
|
990
|
-
|
|
991
|
-
let luminance = 0;
|
|
992
|
-
let finishType = FINISH_TYPE_DEFAULT;
|
|
993
|
-
let edgeMaterial = null;
|
|
994
|
-
const name = lineParser.getToken();
|
|
995
|
-
|
|
996
|
-
if ( ! name ) {
|
|
997
|
-
|
|
998
|
-
throw 'LDrawLoader: Material name was expected after "!COLOUR tag' + lineParser.getLineNumberString() + '.';
|
|
999
|
-
|
|
1000
|
-
} // Parse tag tokens and their parameters
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
let token = null;
|
|
1004
|
-
|
|
1005
|
-
while ( true ) {
|
|
1006
|
-
|
|
1007
|
-
token = lineParser.getToken();
|
|
1008
|
-
|
|
1009
|
-
if ( ! token ) {
|
|
1010
|
-
|
|
1011
|
-
break;
|
|
1012
|
-
|
|
1013
|
-
}
|
|
1014
|
-
|
|
1015
|
-
switch ( token.toUpperCase() ) {
|
|
1016
|
-
|
|
1017
|
-
case 'CODE':
|
|
1018
|
-
code = lineParser.getToken();
|
|
1019
|
-
break;
|
|
1020
|
-
|
|
1021
|
-
case 'VALUE':
|
|
1022
|
-
colour = lineParser.getToken();
|
|
1023
|
-
|
|
1024
|
-
if ( colour.startsWith( '0x' ) ) {
|
|
1025
|
-
|
|
1026
|
-
colour = '#' + colour.substring( 2 );
|
|
1027
|
-
|
|
1028
|
-
} else if ( ! colour.startsWith( '#' ) ) {
|
|
1029
|
-
|
|
1030
|
-
throw 'LDrawLoader: Invalid colour while parsing material' + lineParser.getLineNumberString() + '.';
|
|
1031
|
-
|
|
1032
|
-
}
|
|
1033
|
-
|
|
1034
|
-
break;
|
|
1035
|
-
|
|
1036
|
-
case 'EDGE':
|
|
1037
|
-
edgeColour = lineParser.getToken();
|
|
1038
|
-
|
|
1039
|
-
if ( edgeColour.startsWith( '0x' ) ) {
|
|
1040
|
-
|
|
1041
|
-
edgeColour = '#' + edgeColour.substring( 2 );
|
|
1042
|
-
|
|
1043
|
-
} else if ( ! edgeColour.startsWith( '#' ) ) {
|
|
1044
|
-
|
|
1045
|
-
// Try to see if edge colour is a colour code
|
|
1046
|
-
edgeMaterial = this.getMaterial( edgeColour );
|
|
1047
|
-
|
|
1048
|
-
if ( ! edgeMaterial ) {
|
|
1049
|
-
|
|
1050
|
-
throw 'LDrawLoader: Invalid edge colour while parsing material' + lineParser.getLineNumberString() + '.';
|
|
1051
|
-
|
|
1052
|
-
} // Get the edge material for this triangle material
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
edgeMaterial = edgeMaterial.userData.edgeMaterial;
|
|
1056
|
-
|
|
1057
|
-
}
|
|
1058
|
-
|
|
1059
|
-
break;
|
|
1060
|
-
|
|
1061
|
-
case 'ALPHA':
|
|
1062
|
-
alpha = parseInt( lineParser.getToken() );
|
|
1063
|
-
|
|
1064
|
-
if ( isNaN( alpha ) ) {
|
|
1065
|
-
|
|
1066
|
-
throw 'LDrawLoader: Invalid alpha value in material definition' + lineParser.getLineNumberString() + '.';
|
|
1067
|
-
|
|
1068
|
-
}
|
|
1069
|
-
|
|
1070
|
-
alpha = Math.max( 0, Math.min( 1, alpha / 255 ) );
|
|
1071
|
-
|
|
1072
|
-
if ( alpha < 1 ) {
|
|
1073
|
-
|
|
1074
|
-
isTransparent = true;
|
|
1075
|
-
|
|
1076
|
-
}
|
|
1077
|
-
|
|
1078
|
-
break;
|
|
1079
|
-
|
|
1080
|
-
case 'LUMINANCE':
|
|
1081
|
-
luminance = parseInt( lineParser.getToken() );
|
|
1082
|
-
|
|
1083
|
-
if ( isNaN( luminance ) ) {
|
|
1084
|
-
|
|
1085
|
-
throw 'LDrawLoader: Invalid luminance value in material definition' + LineParser.getLineNumberString() + '.';
|
|
1086
|
-
|
|
1087
|
-
}
|
|
1088
|
-
|
|
1089
|
-
luminance = Math.max( 0, Math.min( 1, luminance / 255 ) );
|
|
1090
|
-
break;
|
|
1091
|
-
|
|
1092
|
-
case 'CHROME':
|
|
1093
|
-
finishType = FINISH_TYPE_CHROME;
|
|
1094
|
-
break;
|
|
1095
|
-
|
|
1096
|
-
case 'PEARLESCENT':
|
|
1097
|
-
finishType = FINISH_TYPE_PEARLESCENT;
|
|
1098
|
-
break;
|
|
1099
|
-
|
|
1100
|
-
case 'RUBBER':
|
|
1101
|
-
finishType = FINISH_TYPE_RUBBER;
|
|
1102
|
-
break;
|
|
1103
|
-
|
|
1104
|
-
case 'MATTE_METALLIC':
|
|
1105
|
-
finishType = FINISH_TYPE_MATTE_METALLIC;
|
|
1106
|
-
break;
|
|
1107
|
-
|
|
1108
|
-
case 'METAL':
|
|
1109
|
-
finishType = FINISH_TYPE_METAL;
|
|
1110
|
-
break;
|
|
1111
|
-
|
|
1112
|
-
case 'MATERIAL':
|
|
1113
|
-
// Not implemented
|
|
1114
|
-
lineParser.setToEnd();
|
|
1115
|
-
break;
|
|
1116
|
-
|
|
1117
|
-
default:
|
|
1118
|
-
throw 'LDrawLoader: Unknown token "' + token + '" while parsing material' + lineParser.getLineNumberString() + '.';
|
|
1119
|
-
break;
|
|
1120
|
-
|
|
1121
|
-
}
|
|
1122
|
-
|
|
1123
|
-
}
|
|
1124
|
-
|
|
1125
|
-
let material = null;
|
|
1126
|
-
|
|
1127
|
-
switch ( finishType ) {
|
|
1128
|
-
|
|
1129
|
-
case FINISH_TYPE_DEFAULT:
|
|
1130
|
-
material = new THREE.MeshStandardMaterial( {
|
|
1131
|
-
color: colour,
|
|
1132
|
-
roughness: 0.3,
|
|
1133
|
-
metalness: 0
|
|
1134
|
-
} );
|
|
1135
|
-
break;
|
|
1136
|
-
|
|
1137
|
-
case FINISH_TYPE_PEARLESCENT:
|
|
1138
|
-
// Try to imitate pearlescency by setting the specular to the complementary of the color, and low shininess
|
|
1139
|
-
const specular = new THREE.Color( colour );
|
|
1140
|
-
const hsl = specular.getHSL( {
|
|
1141
|
-
h: 0,
|
|
1142
|
-
s: 0,
|
|
1143
|
-
l: 0
|
|
1144
|
-
} );
|
|
1145
|
-
hsl.h = ( hsl.h + 0.5 ) % 1;
|
|
1146
|
-
hsl.l = Math.min( 1, hsl.l + ( 1 - hsl.l ) * 0.7 );
|
|
1147
|
-
specular.setHSL( hsl.h, hsl.s, hsl.l );
|
|
1148
|
-
material = new THREE.MeshPhongMaterial( {
|
|
1149
|
-
color: colour,
|
|
1150
|
-
specular: specular,
|
|
1151
|
-
shininess: 10,
|
|
1152
|
-
reflectivity: 0.3
|
|
1153
|
-
} );
|
|
1154
|
-
break;
|
|
1155
|
-
|
|
1156
|
-
case FINISH_TYPE_CHROME:
|
|
1157
|
-
// Mirror finish surface
|
|
1158
|
-
material = new THREE.MeshStandardMaterial( {
|
|
1159
|
-
color: colour,
|
|
1160
|
-
roughness: 0,
|
|
1161
|
-
metalness: 1
|
|
1162
|
-
} );
|
|
1163
|
-
break;
|
|
1164
|
-
|
|
1165
|
-
case FINISH_TYPE_RUBBER:
|
|
1166
|
-
// Rubber finish
|
|
1167
|
-
material = new THREE.MeshStandardMaterial( {
|
|
1168
|
-
color: colour,
|
|
1169
|
-
roughness: 0.9,
|
|
1170
|
-
metalness: 0
|
|
1171
|
-
} );
|
|
1172
|
-
break;
|
|
1499
|
+
setMaterials( materials ) {
|
|
1173
1500
|
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
metalness: 0.4
|
|
1180
|
-
} );
|
|
1181
|
-
break;
|
|
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;
|
|
1182
1506
|
|
|
1183
|
-
|
|
1184
|
-
// Average metal finish
|
|
1185
|
-
material = new THREE.MeshStandardMaterial( {
|
|
1186
|
-
color: colour,
|
|
1187
|
-
roughness: 0.2,
|
|
1188
|
-
metalness: 0.85
|
|
1189
|
-
} );
|
|
1190
|
-
break;
|
|
1507
|
+
}
|
|
1191
1508
|
|
|
1192
|
-
|
|
1193
|
-
// Should not happen
|
|
1194
|
-
break;
|
|
1509
|
+
setFileMap( fileMap ) {
|
|
1195
1510
|
|
|
1196
|
-
|
|
1511
|
+
this.fileMap = fileMap;
|
|
1512
|
+
return this;
|
|
1197
1513
|
|
|
1198
|
-
|
|
1199
|
-
material.premultipliedAlpha = true;
|
|
1200
|
-
material.opacity = alpha;
|
|
1201
|
-
material.depthWrite = ! isTransparent;
|
|
1202
|
-
material.polygonOffset = true;
|
|
1203
|
-
material.polygonOffsetFactor = 1;
|
|
1514
|
+
}
|
|
1204
1515
|
|
|
1205
|
-
|
|
1516
|
+
newParseScopeLevel( materials = null, parentScope = null ) {
|
|
1206
1517
|
|
|
1207
|
-
|
|
1518
|
+
// Adds a new scope level, assign materials to it and returns it
|
|
1519
|
+
const matLib = {};
|
|
1208
1520
|
|
|
1209
|
-
|
|
1521
|
+
if ( materials ) {
|
|
1210
1522
|
|
|
1211
|
-
|
|
1523
|
+
for ( let i = 0, n = materials.length; i < n; i ++ ) {
|
|
1212
1524
|
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
color: edgeColour,
|
|
1216
|
-
transparent: isTransparent,
|
|
1217
|
-
opacity: alpha,
|
|
1218
|
-
depthWrite: ! isTransparent
|
|
1219
|
-
} );
|
|
1220
|
-
edgeMaterial.userData.code = code;
|
|
1221
|
-
edgeMaterial.name = name + ' - Edge'; // This is the material used for conditional edges
|
|
1525
|
+
const material = materials[ i ];
|
|
1526
|
+
matLib[ material.userData.code ] = material;
|
|
1222
1527
|
|
|
1223
|
-
|
|
1224
|
-
fog: true,
|
|
1225
|
-
transparent: isTransparent,
|
|
1226
|
-
depthWrite: ! isTransparent,
|
|
1227
|
-
color: edgeColour,
|
|
1228
|
-
opacity: alpha
|
|
1229
|
-
} );
|
|
1528
|
+
}
|
|
1230
1529
|
|
|
1231
1530
|
}
|
|
1232
1531
|
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
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;
|
|
1237
1561
|
|
|
1238
|
-
}
|
|
1562
|
+
}
|
|
1563
|
+
|
|
1564
|
+
addMaterial( material, parseScope ) {
|
|
1239
1565
|
|
|
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;
|
|
1240
1568
|
|
|
1241
|
-
|
|
1569
|
+
if ( ! matLib[ material.userData.code ] ) {
|
|
1242
1570
|
|
|
1243
|
-
|
|
1244
|
-
const currentParseScope = parseScope;
|
|
1245
|
-
const parentParseScope = currentParseScope.parentScope; // Main colour codes passed to this subobject (or default codes 16 and 24 if it is the root object)
|
|
1571
|
+
this.materials.push( material );
|
|
1246
1572
|
|
|
1247
|
-
|
|
1248
|
-
const mainEdgeColourCode = currentParseScope.mainEdgeColourCode; // Parse result variables
|
|
1573
|
+
}
|
|
1249
1574
|
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
let conditionalSegments;
|
|
1253
|
-
const subobjects = [];
|
|
1254
|
-
let category = null;
|
|
1255
|
-
let keywords = null;
|
|
1575
|
+
matLib[ material.userData.code ] = material;
|
|
1576
|
+
return this;
|
|
1256
1577
|
|
|
1257
|
-
|
|
1578
|
+
}
|
|
1258
1579
|
|
|
1259
|
-
|
|
1260
|
-
text = text.replace( /\r\n/g, '\n' );
|
|
1580
|
+
getMaterial( colorCode, parseScope = this.rootParseScope ) {
|
|
1261
1581
|
|
|
1262
|
-
|
|
1582
|
+
// Given a color code search its material in the parse scopes stack
|
|
1583
|
+
if ( colorCode.startsWith( '0x2' ) ) {
|
|
1263
1584
|
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
let currentEmbeddedFileName = null;
|
|
1268
|
-
let currentEmbeddedText = null;
|
|
1269
|
-
let bfcCertified = false;
|
|
1270
|
-
let bfcCCW = true;
|
|
1271
|
-
let bfcInverted = false;
|
|
1272
|
-
let bfcCull = true;
|
|
1273
|
-
let type = '';
|
|
1274
|
-
let startingConstructionStep = false;
|
|
1275
|
-
const scope = this;
|
|
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 + '' ) );
|
|
1276
1588
|
|
|
1277
|
-
|
|
1589
|
+
}
|
|
1278
1590
|
|
|
1279
|
-
|
|
1280
|
-
let colourCode = lineParser.getToken();
|
|
1591
|
+
while ( parseScope ) {
|
|
1281
1592
|
|
|
1282
|
-
|
|
1593
|
+
const material = parseScope.lib[ colorCode ];
|
|
1283
1594
|
|
|
1284
|
-
|
|
1595
|
+
if ( material ) {
|
|
1285
1596
|
|
|
1286
|
-
|
|
1597
|
+
return material;
|
|
1287
1598
|
|
|
1288
|
-
|
|
1599
|
+
} else {
|
|
1289
1600
|
|
|
1290
|
-
|
|
1601
|
+
parseScope = parseScope.parentScope;
|
|
1291
1602
|
|
|
1292
1603
|
}
|
|
1293
1604
|
|
|
1294
|
-
|
|
1605
|
+
} // Material was not found
|
|
1295
1606
|
|
|
1296
|
-
if ( ! material ) {
|
|
1297
1607
|
|
|
1298
|
-
|
|
1608
|
+
return null;
|
|
1299
1609
|
|
|
1300
|
-
|
|
1610
|
+
}
|
|
1301
1611
|
|
|
1302
|
-
|
|
1612
|
+
parseColorMetaDirective( lineParser ) {
|
|
1303
1613
|
|
|
1304
|
-
|
|
1614
|
+
// Parses a color definition and returns a THREE.Material
|
|
1615
|
+
let code = null; // Triangle and line colors
|
|
1305
1616
|
|
|
1306
|
-
|
|
1617
|
+
let color = 0xFF00FF;
|
|
1618
|
+
let edgeColor = 0xFF00FF; // Transparency
|
|
1307
1619
|
|
|
1308
|
-
|
|
1620
|
+
let alpha = 1;
|
|
1621
|
+
let isTransparent = false; // Self-illumination:
|
|
1309
1622
|
|
|
1310
|
-
|
|
1623
|
+
let luminance = 0;
|
|
1624
|
+
let finishType = FINISH_TYPE_DEFAULT;
|
|
1625
|
+
let edgeMaterial = null;
|
|
1626
|
+
const name = lineParser.getToken();
|
|
1311
1627
|
|
|
1312
|
-
|
|
1628
|
+
if ( ! name ) {
|
|
1313
1629
|
|
|
1314
|
-
|
|
1630
|
+
throw new Error( 'LDrawLoader: Material name was expected after "!COLOUR tag' + lineParser.getLineNumberString() + '.' );
|
|
1315
1631
|
|
|
1316
|
-
|
|
1632
|
+
} // Parse tag tokens and their parameters
|
|
1317
1633
|
|
|
1318
|
-
} // Parse all line commands
|
|
1319
1634
|
|
|
1635
|
+
let token = null;
|
|
1320
1636
|
|
|
1321
|
-
|
|
1637
|
+
while ( true ) {
|
|
1322
1638
|
|
|
1323
|
-
|
|
1324
|
-
if ( line.length === 0 ) continue;
|
|
1639
|
+
token = lineParser.getToken();
|
|
1325
1640
|
|
|
1326
|
-
if (
|
|
1641
|
+
if ( ! token ) {
|
|
1327
1642
|
|
|
1328
|
-
|
|
1643
|
+
break;
|
|
1329
1644
|
|
|
1330
|
-
|
|
1331
|
-
this.cache.setData( currentEmbeddedFileName.toLowerCase(), currentEmbeddedText ); // New embedded text file
|
|
1645
|
+
}
|
|
1332
1646
|
|
|
1333
|
-
|
|
1334
|
-
currentEmbeddedText = '';
|
|
1647
|
+
switch ( token.toUpperCase() ) {
|
|
1335
1648
|
|
|
1336
|
-
|
|
1649
|
+
case 'CODE':
|
|
1650
|
+
code = lineParser.getToken();
|
|
1651
|
+
break;
|
|
1337
1652
|
|
|
1338
|
-
|
|
1653
|
+
case 'VALUE':
|
|
1654
|
+
color = lineParser.getToken();
|
|
1339
1655
|
|
|
1340
|
-
|
|
1656
|
+
if ( color.startsWith( '0x' ) ) {
|
|
1341
1657
|
|
|
1342
|
-
|
|
1658
|
+
color = '#' + color.substring( 2 );
|
|
1343
1659
|
|
|
1344
|
-
|
|
1660
|
+
} else if ( ! color.startsWith( '#' ) ) {
|
|
1345
1661
|
|
|
1346
|
-
|
|
1347
|
-
lp.seekNonSpace();
|
|
1662
|
+
throw new Error( 'LDrawLoader: Invalid color while parsing material' + lineParser.getLineNumberString() + '.' );
|
|
1348
1663
|
|
|
1349
|
-
|
|
1664
|
+
}
|
|
1350
1665
|
|
|
1351
|
-
|
|
1352
|
-
continue;
|
|
1666
|
+
break;
|
|
1353
1667
|
|
|
1354
|
-
|
|
1668
|
+
case 'EDGE':
|
|
1669
|
+
edgeColor = lineParser.getToken();
|
|
1355
1670
|
|
|
1671
|
+
if ( edgeColor.startsWith( '0x' ) ) {
|
|
1356
1672
|
|
|
1357
|
-
|
|
1358
|
-
let material;
|
|
1359
|
-
let segment;
|
|
1360
|
-
let inverted;
|
|
1361
|
-
let ccw;
|
|
1362
|
-
let doubleSided;
|
|
1363
|
-
let v0, v1, v2, v3, c0, c1, faceNormal;
|
|
1673
|
+
edgeColor = '#' + edgeColor.substring( 2 );
|
|
1364
1674
|
|
|
1365
|
-
|
|
1675
|
+
} else if ( ! edgeColor.startsWith( '#' ) ) {
|
|
1366
1676
|
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
// Parse meta directive
|
|
1370
|
-
const meta = lp.getToken();
|
|
1677
|
+
// Try to see if edge color is a color code
|
|
1678
|
+
edgeMaterial = this.getMaterial( edgeColor );
|
|
1371
1679
|
|
|
1372
|
-
|
|
1680
|
+
if ( ! edgeMaterial ) {
|
|
1373
1681
|
|
|
1374
|
-
|
|
1682
|
+
throw new Error( 'LDrawLoader: Invalid edge color while parsing material' + lineParser.getLineNumberString() + '.' );
|
|
1375
1683
|
|
|
1376
|
-
|
|
1377
|
-
type = lp.getToken();
|
|
1378
|
-
currentParseScope.faces = [];
|
|
1379
|
-
currentParseScope.lineSegments = [];
|
|
1380
|
-
currentParseScope.conditionalSegments = [];
|
|
1381
|
-
currentParseScope.type = type;
|
|
1382
|
-
const isRoot = ! parentParseScope.isFromParse;
|
|
1684
|
+
} // Get the edge material for this triangle material
|
|
1383
1685
|
|
|
1384
|
-
if ( isRoot || scope.separateObjects && ! isPrimitiveType( type ) ) {
|
|
1385
1686
|
|
|
1386
|
-
|
|
1387
|
-
currentParseScope.groupObject.userData.startingConstructionStep = currentParseScope.startingConstructionStep;
|
|
1687
|
+
edgeMaterial = edgeMaterial.userData.edgeMaterial;
|
|
1388
1688
|
|
|
1389
|
-
|
|
1390
|
-
// needs to be flipped.
|
|
1689
|
+
}
|
|
1391
1690
|
|
|
1691
|
+
break;
|
|
1392
1692
|
|
|
1393
|
-
|
|
1693
|
+
case 'ALPHA':
|
|
1694
|
+
alpha = parseInt( lineParser.getToken() );
|
|
1394
1695
|
|
|
1395
|
-
|
|
1696
|
+
if ( isNaN( alpha ) ) {
|
|
1396
1697
|
|
|
1397
|
-
|
|
1698
|
+
throw new Error( 'LDrawLoader: Invalid alpha value in material definition' + lineParser.getLineNumberString() + '.' );
|
|
1398
1699
|
|
|
1399
|
-
|
|
1400
|
-
lineSegments = currentParseScope.lineSegments;
|
|
1401
|
-
conditionalSegments = currentParseScope.conditionalSegments;
|
|
1402
|
-
break;
|
|
1700
|
+
}
|
|
1403
1701
|
|
|
1404
|
-
|
|
1405
|
-
material = this.parseColourMetaDirective( lp );
|
|
1702
|
+
alpha = Math.max( 0, Math.min( 1, alpha / 255 ) );
|
|
1406
1703
|
|
|
1407
|
-
|
|
1704
|
+
if ( alpha < 1 ) {
|
|
1408
1705
|
|
|
1409
|
-
|
|
1706
|
+
isTransparent = true;
|
|
1410
1707
|
|
|
1411
|
-
|
|
1708
|
+
}
|
|
1412
1709
|
|
|
1413
|
-
|
|
1710
|
+
break;
|
|
1414
1711
|
|
|
1415
|
-
|
|
1712
|
+
case 'LUMINANCE':
|
|
1713
|
+
luminance = parseInt( lineParser.getToken() );
|
|
1416
1714
|
|
|
1417
|
-
|
|
1715
|
+
if ( isNaN( luminance ) ) {
|
|
1418
1716
|
|
|
1419
|
-
|
|
1420
|
-
category = lp.getToken();
|
|
1421
|
-
break;
|
|
1717
|
+
throw new Error( 'LDrawLoader: Invalid luminance value in material definition' + LineParser.getLineNumberString() + '.' );
|
|
1422
1718
|
|
|
1423
|
-
|
|
1424
|
-
const newKeywords = lp.getRemainingString().split( ',' );
|
|
1719
|
+
}
|
|
1425
1720
|
|
|
1426
|
-
|
|
1721
|
+
luminance = Math.max( 0, Math.min( 1, luminance / 255 ) );
|
|
1722
|
+
break;
|
|
1427
1723
|
|
|
1428
|
-
|
|
1724
|
+
case 'CHROME':
|
|
1725
|
+
finishType = FINISH_TYPE_CHROME;
|
|
1726
|
+
break;
|
|
1429
1727
|
|
|
1430
|
-
|
|
1728
|
+
case 'PEARLESCENT':
|
|
1729
|
+
finishType = FINISH_TYPE_PEARLESCENT;
|
|
1730
|
+
break;
|
|
1431
1731
|
|
|
1432
|
-
|
|
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
|
-
|
|
1744
|
+
case 'MATERIAL':
|
|
1745
|
+
// Not implemented
|
|
1746
|
+
lineParser.setToEnd();
|
|
1747
|
+
break;
|
|
1439
1748
|
|
|
1440
|
-
|
|
1749
|
+
default:
|
|
1750
|
+
throw new Error( 'LDrawLoader: Unknown token "' + token + '" while parsing material' + lineParser.getLineNumberString() + '.' );
|
|
1441
1751
|
|
|
1442
|
-
|
|
1752
|
+
}
|
|
1443
1753
|
|
|
1444
|
-
|
|
1445
|
-
if ( lineIndex > 0 ) {
|
|
1754
|
+
}
|
|
1446
1755
|
|
|
1447
|
-
|
|
1448
|
-
parsingEmbeddedFiles = true;
|
|
1449
|
-
currentEmbeddedFileName = lp.getRemainingString();
|
|
1450
|
-
currentEmbeddedText = '';
|
|
1451
|
-
bfcCertified = false;
|
|
1452
|
-
bfcCCW = true;
|
|
1756
|
+
let material = null;
|
|
1453
1757
|
|
|
1454
|
-
|
|
1758
|
+
switch ( finishType ) {
|
|
1455
1759
|
|
|
1456
|
-
|
|
1760
|
+
case FINISH_TYPE_DEFAULT:
|
|
1761
|
+
material = new THREE.MeshStandardMaterial( {
|
|
1762
|
+
color: color,
|
|
1763
|
+
roughness: 0.3,
|
|
1764
|
+
metalness: 0
|
|
1765
|
+
} );
|
|
1766
|
+
break;
|
|
1457
1767
|
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
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;
|
|
1461
1776
|
|
|
1462
|
-
|
|
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;
|
|
1463
1785
|
|
|
1464
|
-
|
|
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;
|
|
1465
1794
|
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
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;
|
|
1471
1803
|
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
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;
|
|
1476
1812
|
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1813
|
+
default:
|
|
1814
|
+
// Should not happen
|
|
1815
|
+
break;
|
|
1480
1816
|
|
|
1481
|
-
|
|
1482
|
-
case 'NOCLIP':
|
|
1483
|
-
bfcCull = token === 'CLIP';
|
|
1484
|
-
break;
|
|
1817
|
+
}
|
|
1485
1818
|
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1819
|
+
material.transparent = isTransparent;
|
|
1820
|
+
material.premultipliedAlpha = true;
|
|
1821
|
+
material.opacity = alpha;
|
|
1822
|
+
material.depthWrite = ! isTransparent;
|
|
1823
|
+
material.polygonOffset = true;
|
|
1824
|
+
material.polygonOffsetFactor = 1;
|
|
1489
1825
|
|
|
1490
|
-
|
|
1826
|
+
if ( luminance !== 0 ) {
|
|
1491
1827
|
|
|
1492
|
-
|
|
1828
|
+
material.emissive.set( material.color ).multiplyScalar( luminance );
|
|
1493
1829
|
|
|
1494
|
-
|
|
1830
|
+
}
|
|
1495
1831
|
|
|
1496
|
-
|
|
1497
|
-
startingConstructionStep = true;
|
|
1498
|
-
break;
|
|
1832
|
+
if ( ! edgeMaterial ) {
|
|
1499
1833
|
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
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
|
|
1503
1843
|
|
|
1504
|
-
|
|
1844
|
+
edgeMaterial.userData.conditionalEdgeMaterial = new LDrawConditionalLineMaterial( {
|
|
1845
|
+
fog: true,
|
|
1846
|
+
transparent: isTransparent,
|
|
1847
|
+
depthWrite: ! isTransparent,
|
|
1848
|
+
color: edgeColor,
|
|
1849
|
+
opacity: alpha
|
|
1850
|
+
} );
|
|
1505
1851
|
|
|
1506
|
-
|
|
1852
|
+
}
|
|
1507
1853
|
|
|
1508
|
-
|
|
1509
|
-
|
|
1854
|
+
material.userData.code = code;
|
|
1855
|
+
material.name = name;
|
|
1856
|
+
material.userData.edgeMaterial = edgeMaterial;
|
|
1857
|
+
return material;
|
|
1510
1858
|
|
|
1511
|
-
|
|
1512
|
-
material = parseColourCode( lp );
|
|
1513
|
-
const posX = parseFloat( lp.getToken() );
|
|
1514
|
-
const posY = parseFloat( lp.getToken() );
|
|
1515
|
-
const posZ = parseFloat( lp.getToken() );
|
|
1516
|
-
const m0 = parseFloat( lp.getToken() );
|
|
1517
|
-
const m1 = parseFloat( lp.getToken() );
|
|
1518
|
-
const m2 = parseFloat( lp.getToken() );
|
|
1519
|
-
const m3 = parseFloat( lp.getToken() );
|
|
1520
|
-
const m4 = parseFloat( lp.getToken() );
|
|
1521
|
-
const m5 = parseFloat( lp.getToken() );
|
|
1522
|
-
const m6 = parseFloat( lp.getToken() );
|
|
1523
|
-
const m7 = parseFloat( lp.getToken() );
|
|
1524
|
-
const m8 = parseFloat( lp.getToken() );
|
|
1525
|
-
const matrix = new THREE.Matrix4().set( m0, m1, m2, posX, m3, m4, m5, posY, m6, m7, m8, posZ, 0, 0, 0, 1 );
|
|
1526
|
-
let fileName = lp.getRemainingString().trim().replace( /\\/g, '/' );
|
|
1859
|
+
} //
|
|
1527
1860
|
|
|
1528
|
-
if ( scope.fileMap[ fileName ] ) {
|
|
1529
1861
|
|
|
1530
|
-
|
|
1531
|
-
fileName = scope.fileMap[ fileName ];
|
|
1862
|
+
objectParse( info, parseScope ) {
|
|
1532
1863
|
|
|
1533
|
-
|
|
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)
|
|
1534
1867
|
|
|
1535
|
-
|
|
1536
|
-
|
|
1868
|
+
const mainColorCode = currentParseScope.mainColorCode;
|
|
1869
|
+
const mainEdgeColorCode = currentParseScope.mainEdgeColorCode;
|
|
1537
1870
|
|
|
1538
|
-
|
|
1871
|
+
const parseColorCode = ( colorCode, forEdge ) => {
|
|
1539
1872
|
|
|
1540
|
-
|
|
1873
|
+
// Parses next color code and returns a THREE.Material
|
|
1874
|
+
if ( ! forEdge && colorCode === '16' ) {
|
|
1541
1875
|
|
|
1542
|
-
|
|
1876
|
+
colorCode = mainColorCode;
|
|
1543
1877
|
|
|
1544
|
-
|
|
1878
|
+
}
|
|
1545
1879
|
|
|
1546
|
-
|
|
1880
|
+
if ( forEdge && colorCode === '24' ) {
|
|
1547
1881
|
|
|
1548
|
-
|
|
1549
|
-
material: material,
|
|
1550
|
-
matrix: matrix,
|
|
1551
|
-
fileName: fileName,
|
|
1552
|
-
inverted: bfcInverted !== currentParseScope.inverted,
|
|
1553
|
-
startingConstructionStep: startingConstructionStep
|
|
1554
|
-
} );
|
|
1555
|
-
bfcInverted = false;
|
|
1556
|
-
break;
|
|
1557
|
-
// Line type 2: Line segment
|
|
1882
|
+
colorCode = mainEdgeColorCode;
|
|
1558
1883
|
|
|
1559
|
-
|
|
1560
|
-
material = parseColourCode( lp, true );
|
|
1561
|
-
v0 = parseVector( lp );
|
|
1562
|
-
v1 = parseVector( lp );
|
|
1563
|
-
segment = {
|
|
1564
|
-
material: material.userData.edgeMaterial,
|
|
1565
|
-
colourCode: material.userData.code,
|
|
1566
|
-
v0: v0,
|
|
1567
|
-
v1: v1,
|
|
1568
|
-
vertices: [ v0, v1 ]
|
|
1569
|
-
};
|
|
1570
|
-
lineSegments.push( segment );
|
|
1571
|
-
break;
|
|
1572
|
-
// Line type 5: Conditional Line segment
|
|
1884
|
+
}
|
|
1573
1885
|
|
|
1574
|
-
|
|
1575
|
-
material = parseColourCode( lp, true );
|
|
1576
|
-
v0 = parseVector( lp );
|
|
1577
|
-
v1 = parseVector( lp );
|
|
1578
|
-
c0 = parseVector( lp );
|
|
1579
|
-
c1 = parseVector( lp );
|
|
1580
|
-
segment = {
|
|
1581
|
-
material: material.userData.edgeMaterial.userData.conditionalEdgeMaterial,
|
|
1582
|
-
colourCode: material.userData.code,
|
|
1583
|
-
vertices: [ v0, v1 ],
|
|
1584
|
-
controlPoints: [ c0, c1 ]
|
|
1585
|
-
};
|
|
1586
|
-
conditionalSegments.push( segment );
|
|
1587
|
-
break;
|
|
1588
|
-
// Line type 3: Triangle
|
|
1886
|
+
const material = this.getMaterial( colorCode, currentParseScope );
|
|
1589
1887
|
|
|
1590
|
-
|
|
1591
|
-
material = parseColourCode( lp );
|
|
1592
|
-
inverted = currentParseScope.inverted;
|
|
1593
|
-
ccw = bfcCCW !== inverted;
|
|
1594
|
-
doubleSided = ! bfcCertified || ! bfcCull;
|
|
1888
|
+
if ( ! material ) {
|
|
1595
1889
|
|
|
1596
|
-
|
|
1890
|
+
throw new Error( 'LDrawLoader: Unknown color code "' + colorCode + '" is used but it was not defined previously.' );
|
|
1597
1891
|
|
|
1598
|
-
|
|
1599
|
-
v1 = parseVector( lp );
|
|
1600
|
-
v2 = parseVector( lp );
|
|
1892
|
+
}
|
|
1601
1893
|
|
|
1602
|
-
|
|
1894
|
+
return material;
|
|
1603
1895
|
|
|
1604
|
-
|
|
1605
|
-
v1 = parseVector( lp );
|
|
1606
|
-
v0 = parseVector( lp );
|
|
1896
|
+
};
|
|
1607
1897
|
|
|
1608
|
-
|
|
1898
|
+
const faces = info.faces;
|
|
1899
|
+
const lineSegments = info.lineSegments;
|
|
1900
|
+
const conditionalSegments = info.conditionalSegments;
|
|
1901
|
+
const materials = info.materials;
|
|
1609
1902
|
|
|
1610
|
-
|
|
1903
|
+
if ( currentParseScope.inverted ) {
|
|
1611
1904
|
|
|
1612
|
-
|
|
1905
|
+
faces.reverse();
|
|
1613
1906
|
|
|
1614
|
-
|
|
1615
|
-
faces.push( {
|
|
1616
|
-
material: material,
|
|
1617
|
-
colourCode: material.userData.code,
|
|
1618
|
-
faceNormal: faceNormal,
|
|
1619
|
-
vertices: [ v0, v1, v2 ],
|
|
1620
|
-
normals: [ null, null, null ]
|
|
1621
|
-
} );
|
|
1622
|
-
currentParseScope.totalFaces ++;
|
|
1907
|
+
}
|
|
1623
1908
|
|
|
1624
|
-
|
|
1909
|
+
for ( const colorCode in materials ) {
|
|
1625
1910
|
|
|
1626
|
-
|
|
1627
|
-
material: material,
|
|
1628
|
-
colourCode: material.userData.code,
|
|
1629
|
-
faceNormal: faceNormal,
|
|
1630
|
-
vertices: [ v2, v1, v0 ],
|
|
1631
|
-
normals: [ null, null, null ]
|
|
1632
|
-
} );
|
|
1633
|
-
currentParseScope.totalFaces ++;
|
|
1911
|
+
this.addMaterial( materials[ colorCode ], currentParseScope );
|
|
1634
1912
|
|
|
1635
|
-
|
|
1913
|
+
}
|
|
1636
1914
|
|
|
1637
|
-
|
|
1638
|
-
// Line type 4: Quadrilateral
|
|
1915
|
+
for ( let i = 0, l = faces.length; i < l; i ++ ) {
|
|
1639
1916
|
|
|
1640
|
-
|
|
1641
|
-
material = parseColourCode( lp );
|
|
1642
|
-
inverted = currentParseScope.inverted;
|
|
1643
|
-
ccw = bfcCCW !== inverted;
|
|
1644
|
-
doubleSided = ! bfcCertified || ! bfcCull;
|
|
1917
|
+
const face = faces[ i ];
|
|
1645
1918
|
|
|
1646
|
-
|
|
1919
|
+
if ( face.material === null ) {
|
|
1647
1920
|
|
|
1648
|
-
|
|
1649
|
-
v1 = parseVector( lp );
|
|
1650
|
-
v2 = parseVector( lp );
|
|
1651
|
-
v3 = parseVector( lp );
|
|
1921
|
+
face.material = parseColorCode( face.colorCode, false );
|
|
1652
1922
|
|
|
1653
|
-
|
|
1923
|
+
}
|
|
1654
1924
|
|
|
1655
|
-
|
|
1656
|
-
v2 = parseVector( lp );
|
|
1657
|
-
v1 = parseVector( lp );
|
|
1658
|
-
v0 = parseVector( lp );
|
|
1925
|
+
}
|
|
1659
1926
|
|
|
1660
|
-
|
|
1927
|
+
for ( let i = 0, l = lineSegments.length; i < l; i ++ ) {
|
|
1661
1928
|
|
|
1662
|
-
|
|
1929
|
+
const ls = lineSegments[ i ];
|
|
1663
1930
|
|
|
1664
|
-
|
|
1931
|
+
if ( ls.material === null ) {
|
|
1665
1932
|
|
|
1666
|
-
|
|
1667
|
-
// account for the doubling of vertices later when smoothing normals.
|
|
1933
|
+
ls.material = parseColorCode( ls.colorCode, true );
|
|
1668
1934
|
|
|
1669
|
-
|
|
1670
|
-
material: material,
|
|
1671
|
-
colourCode: material.userData.code,
|
|
1672
|
-
faceNormal: faceNormal,
|
|
1673
|
-
vertices: [ v0, v1, v2, v3 ],
|
|
1674
|
-
normals: [ null, null, null, null ]
|
|
1675
|
-
} );
|
|
1676
|
-
currentParseScope.totalFaces += 2;
|
|
1935
|
+
}
|
|
1677
1936
|
|
|
1678
|
-
|
|
1937
|
+
}
|
|
1679
1938
|
|
|
1680
|
-
|
|
1681
|
-
material: material,
|
|
1682
|
-
colourCode: material.userData.code,
|
|
1683
|
-
faceNormal: faceNormal,
|
|
1684
|
-
vertices: [ v3, v2, v1, v0 ],
|
|
1685
|
-
normals: [ null, null, null, null ]
|
|
1686
|
-
} );
|
|
1687
|
-
currentParseScope.totalFaces += 2;
|
|
1939
|
+
for ( let i = 0, l = conditionalSegments.length; i < l; i ++ ) {
|
|
1688
1940
|
|
|
1689
|
-
|
|
1941
|
+
const cs = conditionalSegments[ i ];
|
|
1690
1942
|
|
|
1691
|
-
|
|
1943
|
+
if ( cs.material === null ) {
|
|
1692
1944
|
|
|
1693
|
-
|
|
1694
|
-
throw 'LDrawLoader: Unknown line type "' + lineType + '"' + lp.getLineNumberString() + '.';
|
|
1695
|
-
break;
|
|
1945
|
+
cs.material = parseColorCode( cs.colorCode, true );
|
|
1696
1946
|
|
|
1697
1947
|
}
|
|
1698
1948
|
|
|
1699
1949
|
}
|
|
1700
1950
|
|
|
1701
|
-
|
|
1951
|
+
currentParseScope.faces = info.faces;
|
|
1952
|
+
currentParseScope.conditionalSegments = info.conditionalSegments;
|
|
1953
|
+
currentParseScope.lineSegments = info.lineSegments;
|
|
1954
|
+
currentParseScope.category = info.category;
|
|
1955
|
+
currentParseScope.keywords = info.keywords;
|
|
1956
|
+
currentParseScope.subobjects = info.subobjects;
|
|
1957
|
+
currentParseScope.numSubobjects = info.subobjects.length;
|
|
1958
|
+
currentParseScope.subobjectIndex = 0;
|
|
1959
|
+
currentParseScope.type = info.type;
|
|
1960
|
+
currentParseScope.totalFaces = info.totalFaces;
|
|
1961
|
+
const isRoot = ! parentParseScope.isFromParse;
|
|
1702
1962
|
|
|
1703
|
-
|
|
1963
|
+
if ( isRoot || ! isPrimitiveType( info.type ) ) {
|
|
1704
1964
|
|
|
1705
|
-
|
|
1965
|
+
currentParseScope.groupObject = new THREE.Group();
|
|
1966
|
+
currentParseScope.groupObject.userData.startingConstructionStep = currentParseScope.startingConstructionStep;
|
|
1706
1967
|
|
|
1707
|
-
|
|
1708
|
-
currentParseScope.keywords = keywords;
|
|
1709
|
-
currentParseScope.subobjects = subobjects;
|
|
1710
|
-
currentParseScope.numSubobjects = subobjects.length;
|
|
1711
|
-
currentParseScope.subobjectIndex = 0;
|
|
1968
|
+
}
|
|
1712
1969
|
|
|
1713
1970
|
}
|
|
1714
1971
|
|
|
@@ -1737,21 +1994,32 @@
|
|
|
1737
1994
|
|
|
1738
1995
|
finalizeObject( subobjectParseScope ) {
|
|
1739
1996
|
|
|
1997
|
+
// fail gracefully if an object could not be loaded
|
|
1998
|
+
if ( subobjectParseScope === null ) {
|
|
1999
|
+
|
|
2000
|
+
return;
|
|
2001
|
+
|
|
2002
|
+
}
|
|
2003
|
+
|
|
1740
2004
|
const parentParseScope = subobjectParseScope.parentScope; // Smooth the normals if this is a part or if this is a case where the subpart
|
|
1741
2005
|
// is added directly into the parent model (meaning it will never get smoothed by
|
|
1742
2006
|
// being added to a part)
|
|
1743
2007
|
|
|
1744
|
-
const doSmooth = isPartType( subobjectParseScope.type ) ||
|
|
2008
|
+
const doSmooth = isPartType( subobjectParseScope.type ) || isPrimitiveType( subobjectParseScope.type ) && isModelType( subobjectParseScope.parentScope.type );
|
|
1745
2009
|
|
|
1746
2010
|
if ( this.smoothNormals && doSmooth ) {
|
|
1747
2011
|
|
|
1748
|
-
|
|
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 );
|
|
1749
2017
|
|
|
1750
2018
|
}
|
|
1751
2019
|
|
|
1752
2020
|
const isRoot = ! parentParseScope.isFromParse;
|
|
1753
2021
|
|
|
1754
|
-
if (
|
|
2022
|
+
if ( ! isPrimitiveType( subobjectParseScope.type ) || isRoot ) {
|
|
1755
2023
|
|
|
1756
2024
|
const objGroup = subobjectParseScope.groupObject;
|
|
1757
2025
|
|
|
@@ -1785,26 +2053,23 @@
|
|
|
1785
2053
|
|
|
1786
2054
|
} else {
|
|
1787
2055
|
|
|
1788
|
-
const separateObjects = this.separateObjects;
|
|
1789
2056
|
const parentLineSegments = parentParseScope.lineSegments;
|
|
1790
2057
|
const parentConditionalSegments = parentParseScope.conditionalSegments;
|
|
1791
2058
|
const parentFaces = parentParseScope.faces;
|
|
2059
|
+
const parentFaceMaterials = parentParseScope.faceMaterials;
|
|
1792
2060
|
const lineSegments = subobjectParseScope.lineSegments;
|
|
1793
2061
|
const conditionalSegments = subobjectParseScope.conditionalSegments;
|
|
1794
2062
|
const faces = subobjectParseScope.faces;
|
|
2063
|
+
const faceMaterials = subobjectParseScope.faceMaterials;
|
|
2064
|
+
const matrix = subobjectParseScope.matrix;
|
|
2065
|
+
const matrixScaleInverted = matrix.determinant() < 0;
|
|
1795
2066
|
|
|
1796
2067
|
for ( let i = 0, l = lineSegments.length; i < l; i ++ ) {
|
|
1797
2068
|
|
|
1798
2069
|
const ls = lineSegments[ i ];
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
const vertices = ls.vertices;
|
|
1803
|
-
vertices[ 0 ].applyMatrix4( subobjectParseScope.matrix );
|
|
1804
|
-
vertices[ 1 ].applyMatrix4( subobjectParseScope.matrix );
|
|
1805
|
-
|
|
1806
|
-
}
|
|
1807
|
-
|
|
2070
|
+
const vertices = ls.vertices;
|
|
2071
|
+
vertices[ 0 ].applyMatrix4( matrix );
|
|
2072
|
+
vertices[ 1 ].applyMatrix4( matrix );
|
|
1808
2073
|
parentLineSegments.push( ls );
|
|
1809
2074
|
|
|
1810
2075
|
}
|
|
@@ -1812,18 +2077,12 @@
|
|
|
1812
2077
|
for ( let i = 0, l = conditionalSegments.length; i < l; i ++ ) {
|
|
1813
2078
|
|
|
1814
2079
|
const os = conditionalSegments[ i ];
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
vertices[ 1 ].applyMatrix4( subobjectParseScope.matrix );
|
|
1822
|
-
controlPoints[ 0 ].applyMatrix4( subobjectParseScope.matrix );
|
|
1823
|
-
controlPoints[ 1 ].applyMatrix4( subobjectParseScope.matrix );
|
|
1824
|
-
|
|
1825
|
-
}
|
|
1826
|
-
|
|
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 );
|
|
1827
2086
|
parentConditionalSegments.push( os );
|
|
1828
2087
|
|
|
1829
2088
|
}
|
|
@@ -1831,22 +2090,19 @@
|
|
|
1831
2090
|
for ( let i = 0, l = faces.length; i < l; i ++ ) {
|
|
1832
2091
|
|
|
1833
2092
|
const tri = faces[ i ];
|
|
2093
|
+
const vertices = tri.vertices;
|
|
1834
2094
|
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
const vertices = tri.vertices;
|
|
2095
|
+
for ( let i = 0, l = vertices.length; i < l; i ++ ) {
|
|
1838
2096
|
|
|
1839
|
-
|
|
2097
|
+
vertices[ i ].applyMatrix4( matrix );
|
|
1840
2098
|
|
|
1841
|
-
|
|
2099
|
+
} // If the scale of the object is negated then the triangle winding order
|
|
2100
|
+
// needs to be flipped.
|
|
1842
2101
|
|
|
1843
|
-
}
|
|
1844
|
-
|
|
1845
|
-
_tempVec0.subVectors( vertices[ 1 ], vertices[ 0 ] );
|
|
1846
2102
|
|
|
1847
|
-
|
|
2103
|
+
if ( matrixScaleInverted !== subobjectParseScope.inverted ) {
|
|
1848
2104
|
|
|
1849
|
-
|
|
2105
|
+
vertices.reverse();
|
|
1850
2106
|
|
|
1851
2107
|
}
|
|
1852
2108
|
|
|
@@ -1855,12 +2111,13 @@
|
|
|
1855
2111
|
}
|
|
1856
2112
|
|
|
1857
2113
|
parentParseScope.totalFaces += subobjectParseScope.totalFaces;
|
|
2114
|
+
faceMaterials.forEach( material => parentFaceMaterials.add( material ) );
|
|
1858
2115
|
|
|
1859
2116
|
}
|
|
1860
2117
|
|
|
1861
2118
|
}
|
|
1862
2119
|
|
|
1863
|
-
async processObject(
|
|
2120
|
+
async processObject( parsedInfo, subobject, url, parentScope ) {
|
|
1864
2121
|
|
|
1865
2122
|
const scope = this;
|
|
1866
2123
|
const parseScope = this.newParseScopeLevel( null, parentScope );
|
|
@@ -1869,18 +2126,28 @@
|
|
|
1869
2126
|
|
|
1870
2127
|
if ( subobject ) {
|
|
1871
2128
|
|
|
1872
|
-
parseScope.currentMatrix.multiplyMatrices( parentParseScope.currentMatrix, subobject.matrix );
|
|
1873
2129
|
parseScope.matrix.copy( subobject.matrix );
|
|
1874
2130
|
parseScope.inverted = subobject.inverted;
|
|
1875
2131
|
parseScope.startingConstructionStep = subobject.startingConstructionStep;
|
|
1876
|
-
parseScope.mainColourCode = subobject.material.userData.code;
|
|
1877
|
-
parseScope.mainEdgeColourCode = subobject.material.userData.edgeMaterial.userData.code;
|
|
1878
2132
|
parseScope.fileName = subobject.fileName;
|
|
1879
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
|
+
|
|
1880
2147
|
} // Parse the object
|
|
1881
2148
|
|
|
1882
2149
|
|
|
1883
|
-
this.objectParse(
|
|
2150
|
+
this.objectParse( parsedInfo, parseScope );
|
|
1884
2151
|
const subobjects = parseScope.subobjects;
|
|
1885
2152
|
const promises = [];
|
|
1886
2153
|
|
|
@@ -1912,13 +2179,14 @@
|
|
|
1912
2179
|
|
|
1913
2180
|
function loadSubobject( subobject ) {
|
|
1914
2181
|
|
|
1915
|
-
return scope.
|
|
2182
|
+
return scope.parseCache.loadData( subobject.fileName ).then( function ( parsedInfo ) {
|
|
1916
2183
|
|
|
1917
|
-
return scope.processObject(
|
|
2184
|
+
return scope.processObject( parsedInfo, subobject, url, parseScope );
|
|
1918
2185
|
|
|
1919
|
-
} ).catch( function () {
|
|
2186
|
+
} ).catch( function ( err ) {
|
|
1920
2187
|
|
|
1921
|
-
console.warn(
|
|
2188
|
+
console.warn( err );
|
|
2189
|
+
return null;
|
|
1922
2190
|
|
|
1923
2191
|
} );
|
|
1924
2192
|
|