super-three 0.136.0 → 0.141.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (768) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +24 -26
  3. package/build/three.cjs +35889 -0
  4. package/build/three.js +4837 -6036
  5. package/build/three.min.js +2 -2
  6. package/build/three.module.js +16548 -17930
  7. package/examples/js/animation/CCDIKSolver.js +3 -2
  8. package/examples/js/animation/MMDAnimationHelper.js +3 -1
  9. package/examples/js/animation/MMDPhysics.js +3 -3
  10. package/examples/js/cameras/CinematicCamera.js +2 -7
  11. package/examples/js/controls/ArcballControls.js +3 -1
  12. package/examples/js/controls/PointerLockControls.js +3 -2
  13. package/examples/js/controls/TrackballControls.js +1 -4
  14. package/examples/js/controls/TransformControls.js +5 -6
  15. package/examples/js/controls/experimental/CameraControls.js +8 -4
  16. package/examples/js/csm/CSMFrustum.js +1 -1
  17. package/examples/js/csm/CSMShader.js +51 -35
  18. package/examples/js/curves/CurveExtras.js +14 -18
  19. package/examples/js/effects/AnaglyphEffect.js +7 -4
  20. package/examples/js/effects/AsciiEffect.js +10 -29
  21. package/examples/js/exporters/ColladaExporter.js +29 -7
  22. package/examples/js/exporters/EXRExporter.js +458 -0
  23. package/examples/js/exporters/GLTFExporter.js +233 -105
  24. package/examples/js/exporters/MMDExporter.js +1 -1
  25. package/examples/js/exporters/OBJExporter.js +12 -18
  26. package/examples/js/exporters/PLYExporter.js +15 -20
  27. package/examples/js/exporters/USDZExporter.js +13 -0
  28. package/examples/js/geometries/ConvexGeometry.js +1 -1
  29. package/examples/js/geometries/DecalGeometry.js +0 -7
  30. package/examples/js/geometries/LightningStrike.js +2 -2
  31. package/examples/js/geometries/ParametricGeometries.js +3 -3
  32. package/examples/js/geometries/TextGeometry.js +12 -10
  33. package/examples/js/helpers/OctreeHelper.js +74 -0
  34. package/examples/js/helpers/VertexNormalsHelper.js +4 -23
  35. package/examples/js/helpers/VertexTangentsHelper.js +3 -13
  36. package/examples/js/helpers/ViewHelper.js +266 -0
  37. package/examples/js/interactive/HTMLMesh.js +249 -42
  38. package/examples/js/interactive/SelectionHelper.js +1 -1
  39. package/examples/js/libs/opentype.min.js +1 -118
  40. package/examples/js/lines/Line2.js +1 -2
  41. package/examples/js/lines/LineGeometry.js +10 -21
  42. package/examples/js/lines/LineMaterial.js +2 -3
  43. package/examples/js/lines/LineSegments2.js +214 -140
  44. package/examples/js/lines/LineSegmentsGeometry.js +3 -14
  45. package/examples/js/lines/Wireframe.js +2 -3
  46. package/examples/js/lines/WireframeGeometry2.js +1 -2
  47. package/examples/js/loaders/3MFLoader.js +21 -17
  48. package/examples/js/loaders/AMFLoader.js +1 -1
  49. package/examples/js/loaders/BasisTextureLoader.js +1 -0
  50. package/examples/js/loaders/ColladaLoader.js +33 -12
  51. package/examples/js/loaders/DDSLoader.js +23 -30
  52. package/examples/js/loaders/EXRLoader.js +314 -294
  53. package/examples/js/loaders/FBXLoader.js +11 -0
  54. package/examples/js/loaders/FontLoader.js +1 -2
  55. package/examples/js/loaders/GCodeLoader.js +0 -1
  56. package/examples/js/loaders/GLTFLoader.js +250 -61
  57. package/examples/js/loaders/HDRCubeTextureLoader.js +0 -2
  58. package/examples/js/loaders/KMZLoader.js +1 -1
  59. package/examples/js/loaders/KTX2Loader.js +1 -9
  60. package/examples/js/loaders/LDrawLoader.js +1342 -962
  61. package/examples/js/loaders/LUT3dlLoader.js +18 -11
  62. package/examples/js/loaders/LUTCubeLoader.js +4 -5
  63. package/examples/js/loaders/LWOLoader.js +4 -2
  64. package/examples/js/loaders/LogLuvLoader.js +1 -2
  65. package/examples/js/loaders/MD2Loader.js +5 -5
  66. package/examples/js/loaders/MMDLoader.js +18 -4
  67. package/examples/js/loaders/MTLLoader.js +10 -3
  68. package/examples/js/loaders/NRRDLoader.js +1 -1
  69. package/examples/js/loaders/OBJLoader.js +9 -5
  70. package/examples/js/loaders/PCDLoader.js +24 -15
  71. package/examples/js/loaders/PDBLoader.js +11 -11
  72. package/examples/js/loaders/PLYLoader.js +5 -1
  73. package/examples/js/loaders/RGBELoader.js +9 -17
  74. package/examples/js/loaders/RGBMLoader.js +13 -16
  75. package/examples/js/loaders/STLLoader.js +1 -1
  76. package/examples/js/loaders/SVGLoader.js +21 -11
  77. package/examples/js/loaders/VOXLoader.js +3 -4
  78. package/examples/js/loaders/VRMLLoader.js +9 -21
  79. package/examples/js/loaders/VTKLoader.js +260 -295
  80. package/examples/js/loaders/lwo/LWO2Parser.js +4 -4
  81. package/examples/js/loaders/lwo/LWO3Parser.js +4 -4
  82. package/examples/js/math/ConvexHull.js +13 -28
  83. package/examples/js/math/Lut.js +27 -26
  84. package/examples/js/misc/MorphAnimMesh.js +2 -2
  85. package/examples/js/misc/ProgressiveLightMap.js +1 -1
  86. package/examples/js/misc/Volume.js +39 -48
  87. package/examples/js/misc/VolumeSlice.js +21 -21
  88. package/examples/js/modifiers/CurveModifier.js +8 -7
  89. package/examples/js/modifiers/EdgeSplitModifier.js +0 -13
  90. package/examples/js/modifiers/SimplifyModifier.js +1 -18
  91. package/examples/js/modifiers/TessellateModifier.js +0 -7
  92. package/examples/js/objects/Lensflare.js +4 -4
  93. package/examples/js/objects/LightningStorm.js +4 -5
  94. package/examples/js/objects/MarchingCubes.js +6 -11
  95. package/examples/js/objects/Reflector.js +10 -16
  96. package/examples/js/objects/ReflectorForSSRPass.js +1 -11
  97. package/examples/js/objects/Refractor.js +10 -16
  98. package/examples/js/objects/ShadowMesh.js +1 -2
  99. package/examples/js/objects/Sky.js +1 -1
  100. package/examples/js/objects/Water.js +2 -15
  101. package/examples/js/objects/Water2.js +1 -1
  102. package/examples/js/postprocessing/AdaptiveToneMappingPass.js +6 -10
  103. package/examples/js/postprocessing/AfterimagePass.js +2 -6
  104. package/examples/js/postprocessing/BloomPass.js +50 -19
  105. package/examples/js/postprocessing/DotScreenPass.js +1 -1
  106. package/examples/js/postprocessing/EffectComposer.js +1 -6
  107. package/examples/js/postprocessing/GlitchPass.js +4 -5
  108. package/examples/js/postprocessing/LUTPass.js +2 -2
  109. package/examples/js/postprocessing/OutlinePass.js +8 -17
  110. package/examples/js/postprocessing/SAOPass.js +2 -7
  111. package/examples/js/postprocessing/SMAAPass.js +2 -9
  112. package/examples/js/postprocessing/SSAARenderPass.js +1 -6
  113. package/examples/js/postprocessing/SSAOPass.js +9 -21
  114. package/examples/js/postprocessing/SSRPass.js +3 -8
  115. package/examples/js/postprocessing/SavePass.js +1 -5
  116. package/examples/js/postprocessing/TAARenderPass.js +4 -4
  117. package/examples/js/postprocessing/UnrealBloomPass.js +3 -12
  118. package/examples/js/renderers/CSS2DRenderer.js +20 -19
  119. package/examples/js/renderers/CSS3DRenderer.js +40 -36
  120. package/examples/js/renderers/Projector.js +97 -124
  121. package/examples/js/renderers/SVGRenderer.js +3 -4
  122. package/examples/js/shaders/BlendShader.js +2 -1
  123. package/examples/js/shaders/CopyShader.js +4 -3
  124. package/examples/js/shaders/DigitalGlitch.js +3 -3
  125. package/examples/js/shaders/FXAAShader.js +239 -1069
  126. package/examples/js/shaders/FreiChenShader.js +1 -1
  127. package/examples/js/shaders/HorizontalBlurShader.js +1 -1
  128. package/examples/js/shaders/HorizontalTiltShiftShader.js +1 -1
  129. package/examples/js/shaders/MMDToonShader.js +0 -1
  130. package/examples/js/shaders/SSAOShader.js +1 -1
  131. package/examples/js/shaders/SSRShader.js +3 -3
  132. package/examples/js/shaders/ToneMapShader.js +1 -1
  133. package/examples/js/utils/BufferGeometryUtils.js +282 -22
  134. package/examples/js/utils/GPUStatsPanel.js +1 -1
  135. package/examples/js/utils/LDrawUtils.js +182 -0
  136. package/examples/js/utils/SceneUtils.js +59 -0
  137. package/examples/js/utils/SkeletonUtils.js +1 -1
  138. package/examples/js/utils/UVsDebug.js +23 -33
  139. package/examples/jsm/animation/CCDIKSolver.js +3 -9
  140. package/examples/jsm/animation/MMDPhysics.js +2 -2
  141. package/examples/jsm/cameras/CinematicCamera.js +2 -5
  142. package/examples/jsm/{WebGL.js → capabilities/WebGL.js} +2 -2
  143. package/examples/jsm/{renderers/webgpu → capabilities}/WebGPU.js +7 -1
  144. package/examples/jsm/controls/ArcballControls.js +3 -1
  145. package/examples/jsm/controls/PointerLockControls.js +4 -2
  146. package/examples/jsm/controls/TrackballControls.js +1 -4
  147. package/examples/jsm/controls/TransformControls.js +6 -6
  148. package/examples/jsm/controls/experimental/CameraControls.js +2 -2
  149. package/examples/jsm/csm/CSMFrustum.js +1 -1
  150. package/examples/jsm/csm/CSMShader.js +51 -35
  151. package/examples/jsm/curves/CurveExtras.js +15 -17
  152. package/examples/jsm/effects/AnaglyphEffect.js +4 -4
  153. package/examples/jsm/effects/AsciiEffect.js +9 -22
  154. package/examples/jsm/exporters/ColladaExporter.js +31 -6
  155. package/examples/jsm/exporters/EXRExporter.js +507 -0
  156. package/examples/jsm/exporters/GLTFExporter.js +261 -103
  157. package/examples/jsm/exporters/KTX2Exporter.js +281 -0
  158. package/examples/jsm/exporters/MMDExporter.js +1 -1
  159. package/examples/jsm/exporters/OBJExporter.js +11 -18
  160. package/examples/jsm/exporters/PLYExporter.js +23 -21
  161. package/examples/jsm/exporters/USDZExporter.js +12 -0
  162. package/examples/jsm/geometries/ConvexGeometry.js +1 -1
  163. package/examples/jsm/geometries/DecalGeometry.js +0 -7
  164. package/examples/jsm/geometries/LightningStrike.js +2 -2
  165. package/examples/jsm/geometries/ParametricGeometries.js +3 -3
  166. package/examples/jsm/geometries/TextGeometry.js +13 -13
  167. package/examples/jsm/helpers/OctreeHelper.js +58 -0
  168. package/examples/jsm/helpers/VertexNormalsHelper.js +4 -25
  169. package/examples/jsm/helpers/VertexTangentsHelper.js +3 -15
  170. package/examples/jsm/helpers/ViewHelper.js +295 -0
  171. package/examples/jsm/interactive/HTMLMesh.js +268 -36
  172. package/examples/jsm/interactive/SelectionHelper.js +1 -1
  173. package/examples/jsm/libs/flow.module.js +947 -183
  174. package/examples/jsm/libs/ktx-parse.module.js +1 -1
  175. package/examples/jsm/libs/lil-gui.module.min.js +2 -2
  176. package/examples/jsm/libs/mikktspace.module.js +128 -0
  177. package/examples/jsm/libs/opentype.module.js +14568 -0
  178. package/examples/jsm/lines/Line2.js +2 -2
  179. package/examples/jsm/lines/LineGeometry.js +11 -19
  180. package/examples/jsm/lines/LineMaterial.js +3 -3
  181. package/examples/jsm/lines/LineSegments2.js +210 -145
  182. package/examples/jsm/lines/LineSegmentsGeometry.js +3 -12
  183. package/examples/jsm/lines/Wireframe.js +3 -3
  184. package/examples/jsm/lines/WireframeGeometry2.js +2 -2
  185. package/examples/jsm/loaders/3DMLoader.js +3 -2
  186. package/examples/jsm/loaders/3MFLoader.js +21 -17
  187. package/examples/jsm/loaders/AMFLoader.js +1 -1
  188. package/examples/jsm/loaders/BasisTextureLoader.js +7 -0
  189. package/examples/jsm/loaders/ColladaLoader.js +40 -12
  190. package/examples/jsm/loaders/DDSLoader.js +21 -28
  191. package/examples/jsm/loaders/EXRLoader.js +309 -293
  192. package/examples/jsm/loaders/FBXLoader.js +11 -0
  193. package/examples/jsm/loaders/FontLoader.js +2 -2
  194. package/examples/jsm/loaders/GCodeLoader.js +0 -1
  195. package/examples/jsm/loaders/GLTFLoader.js +263 -68
  196. package/examples/jsm/loaders/HDRCubeTextureLoader.js +1 -4
  197. package/examples/jsm/loaders/KMZLoader.js +1 -1
  198. package/examples/jsm/loaders/KTX2Loader.js +1 -10
  199. package/examples/jsm/loaders/LDrawLoader.js +1335 -988
  200. package/examples/jsm/loaders/LUT3dlLoader.js +19 -14
  201. package/examples/jsm/loaders/LUTCubeLoader.js +5 -7
  202. package/examples/jsm/loaders/LWOLoader.js +5 -3
  203. package/examples/jsm/loaders/LogLuvLoader.js +2 -2
  204. package/examples/jsm/loaders/MD2Loader.js +5 -5
  205. package/examples/jsm/loaders/MMDLoader.js +21 -3
  206. package/examples/jsm/loaders/MTLLoader.js +11 -4
  207. package/examples/jsm/loaders/NRRDLoader.js +1 -2
  208. package/examples/jsm/loaders/OBJLoader.js +11 -7
  209. package/examples/jsm/loaders/PCDLoader.js +25 -17
  210. package/examples/jsm/loaders/PDBLoader.js +11 -11
  211. package/examples/jsm/loaders/PLYLoader.js +10 -2
  212. package/examples/jsm/loaders/RGBELoader.js +11 -20
  213. package/examples/jsm/loaders/RGBMLoader.js +15 -12
  214. package/examples/jsm/loaders/STLLoader.js +1 -1
  215. package/examples/jsm/loaders/SVGLoader.js +26 -11
  216. package/examples/jsm/loaders/TTFLoader.js +1 -1
  217. package/examples/jsm/loaders/VOXLoader.js +5 -5
  218. package/examples/jsm/loaders/VRMLLoader.js +9 -23
  219. package/examples/jsm/loaders/VTKLoader.js +256 -284
  220. package/examples/jsm/loaders/ifc/web-ifc-api.js +9 -8
  221. package/examples/jsm/loaders/lwo/LWO2Parser.js +4 -4
  222. package/examples/jsm/loaders/lwo/LWO3Parser.js +4 -4
  223. package/examples/jsm/math/ConvexHull.js +13 -28
  224. package/examples/jsm/math/Lut.js +35 -25
  225. package/examples/jsm/misc/MorphAnimMesh.js +2 -2
  226. package/examples/jsm/misc/ProgressiveLightMap.js +1 -1
  227. package/examples/jsm/misc/Volume.js +39 -46
  228. package/examples/jsm/misc/VolumeSlice.js +21 -21
  229. package/examples/jsm/modifiers/CurveModifier.js +9 -8
  230. package/examples/jsm/modifiers/EdgeSplitModifier.js +0 -13
  231. package/examples/jsm/modifiers/SimplifyModifier.js +1 -18
  232. package/examples/jsm/modifiers/TessellateModifier.js +0 -7
  233. package/examples/jsm/node-editor/NodeEditor.js +485 -163
  234. package/examples/jsm/node-editor/accessors/MatcapUVEditor.js +14 -0
  235. package/examples/jsm/node-editor/accessors/NormalEditor.js +9 -9
  236. package/examples/jsm/node-editor/accessors/PositionEditor.js +9 -9
  237. package/examples/jsm/node-editor/accessors/UVEditor.js +7 -8
  238. package/examples/jsm/node-editor/core/BaseNode.js +89 -0
  239. package/examples/jsm/node-editor/core/DataFile.js +59 -0
  240. package/examples/jsm/node-editor/core/FileEditor.js +20 -0
  241. package/examples/jsm/node-editor/core/FileURLEditor.js +29 -0
  242. package/examples/jsm/node-editor/display/BlendEditor.js +10 -9
  243. package/examples/jsm/node-editor/display/NormalMapEditor.js +49 -0
  244. package/examples/jsm/node-editor/examples/animate-uv.json +1 -1
  245. package/examples/jsm/node-editor/examples/fake-top-light.json +1 -1
  246. package/examples/jsm/node-editor/examples/matcap.json +1 -0
  247. package/examples/jsm/node-editor/examples/oscillator-color.json +1 -1
  248. package/examples/jsm/node-editor/examples/rim.json +1 -1
  249. package/examples/jsm/node-editor/inputs/ColorEditor.js +18 -13
  250. package/examples/jsm/node-editor/inputs/FloatEditor.js +8 -9
  251. package/examples/jsm/node-editor/inputs/SliderEditor.js +6 -7
  252. package/examples/jsm/node-editor/inputs/TextureEditor.js +155 -0
  253. package/examples/jsm/node-editor/inputs/Vector2Editor.js +9 -9
  254. package/examples/jsm/node-editor/inputs/Vector3Editor.js +11 -11
  255. package/examples/jsm/node-editor/inputs/Vector4Editor.js +12 -12
  256. package/examples/jsm/node-editor/materials/BasicMaterialEditor.js +87 -0
  257. package/examples/jsm/node-editor/materials/PointsMaterialEditor.js +102 -0
  258. package/examples/jsm/node-editor/materials/StandardMaterialEditor.js +31 -18
  259. package/examples/jsm/node-editor/math/AngleEditor.js +40 -0
  260. package/examples/jsm/node-editor/math/DotEditor.js +8 -9
  261. package/examples/jsm/node-editor/math/InvertEditor.js +8 -7
  262. package/examples/jsm/node-editor/math/LimiterEditor.js +28 -14
  263. package/examples/jsm/node-editor/math/NormalizeEditor.js +9 -7
  264. package/examples/jsm/node-editor/math/OperatorEditor.js +29 -16
  265. package/examples/jsm/node-editor/math/PowerEditor.js +20 -10
  266. package/examples/jsm/node-editor/math/TrigonometryEditor.js +15 -9
  267. package/examples/jsm/node-editor/procedural/CheckerEditor.js +7 -6
  268. package/examples/jsm/node-editor/scene/MeshEditor.js +99 -0
  269. package/examples/jsm/node-editor/scene/Object3DEditor.js +160 -0
  270. package/examples/jsm/node-editor/scene/PointsEditor.js +99 -0
  271. package/examples/jsm/node-editor/utils/JoinEditor.js +58 -0
  272. package/examples/jsm/node-editor/utils/OscillatorEditor.js +9 -8
  273. package/examples/jsm/node-editor/utils/PreviewEditor.js +166 -0
  274. package/examples/jsm/node-editor/utils/SplitEditor.js +39 -0
  275. package/examples/jsm/node-editor/utils/TimerEditor.js +7 -6
  276. package/examples/jsm/nodes/Nodes.js +283 -94
  277. package/examples/jsm/nodes/accessors/BufferNode.js +24 -0
  278. package/examples/jsm/nodes/accessors/CameraNode.js +27 -196
  279. package/examples/jsm/nodes/accessors/CubeTextureNode.js +103 -0
  280. package/examples/jsm/nodes/accessors/InstanceNode.js +58 -0
  281. package/examples/jsm/{renderers/nodes → nodes}/accessors/MaterialNode.js +49 -11
  282. package/examples/jsm/{renderers/nodes → nodes}/accessors/MaterialReferenceNode.js +0 -0
  283. package/examples/jsm/{renderers/nodes → nodes}/accessors/ModelNode.js +8 -0
  284. package/examples/jsm/{renderers/nodes → nodes}/accessors/ModelViewProjectionNode.js +0 -0
  285. package/examples/jsm/nodes/accessors/NormalNode.js +39 -99
  286. package/examples/jsm/{renderers/nodes → nodes}/accessors/Object3DNode.js +34 -17
  287. package/examples/jsm/{renderers/nodes → nodes}/accessors/PointUVNode.js +2 -2
  288. package/examples/jsm/nodes/accessors/PositionNode.js +41 -110
  289. package/examples/jsm/nodes/accessors/ReferenceNode.js +65 -0
  290. package/examples/jsm/nodes/accessors/ReflectNode.js +34 -121
  291. package/examples/jsm/nodes/accessors/SkinningNode.js +102 -0
  292. package/examples/jsm/nodes/accessors/StorageBufferNode.js +21 -0
  293. package/examples/jsm/nodes/accessors/TextureNode.js +100 -0
  294. package/examples/jsm/nodes/accessors/UVNode.js +17 -43
  295. package/examples/jsm/nodes/accessors/UserDataNode.js +23 -0
  296. package/examples/jsm/nodes/core/ArrayUniformNode.js +23 -0
  297. package/examples/jsm/nodes/core/AttributeNode.js +33 -30
  298. package/examples/jsm/{renderers/nodes → nodes}/core/BypassNode.js +2 -2
  299. package/examples/jsm/{renderers/nodes → nodes}/core/CodeNode.js +2 -30
  300. package/examples/jsm/nodes/core/ConstNode.js +10 -106
  301. package/examples/jsm/{renderers/nodes → nodes}/core/ContextNode.js +17 -3
  302. package/examples/jsm/nodes/core/ExpressionNode.js +24 -7
  303. package/examples/jsm/nodes/core/FunctionCallNode.js +26 -67
  304. package/examples/jsm/nodes/core/FunctionNode.js +43 -216
  305. package/examples/jsm/nodes/core/InputNode.js +28 -60
  306. package/examples/jsm/nodes/core/InstanceIndexNode.js +21 -0
  307. package/examples/jsm/nodes/core/Node.js +228 -94
  308. package/examples/jsm/{renderers/nodes → nodes}/core/NodeAttribute.js +2 -2
  309. package/examples/jsm/nodes/core/NodeBuilder.js +388 -560
  310. package/examples/jsm/{renderers/nodes → nodes}/core/NodeCode.js +0 -0
  311. package/examples/jsm/nodes/core/NodeFrame.js +30 -23
  312. package/examples/jsm/{renderers/nodes → nodes}/core/NodeFunction.js +0 -0
  313. package/examples/jsm/{renderers/nodes → nodes}/core/NodeFunctionInput.js +0 -0
  314. package/examples/jsm/{renderers/nodes → nodes}/core/NodeKeywords.js +0 -0
  315. package/examples/jsm/{renderers/nodes → nodes}/core/NodeParser.js +0 -0
  316. package/examples/jsm/nodes/core/NodeUniform.js +8 -6
  317. package/examples/jsm/nodes/core/NodeUtils.js +69 -37
  318. package/examples/jsm/{renderers/nodes → nodes}/core/NodeVar.js +2 -2
  319. package/examples/jsm/{renderers/nodes → nodes}/core/NodeVary.js +2 -2
  320. package/examples/jsm/{renderers/nodes → nodes}/core/PropertyNode.js +0 -0
  321. package/examples/jsm/nodes/core/TempNode.js +20 -112
  322. package/examples/jsm/nodes/core/UniformNode.js +46 -0
  323. package/examples/jsm/nodes/core/VarNode.js +58 -27
  324. package/examples/jsm/{renderers/nodes → nodes}/core/VaryNode.js +0 -0
  325. package/examples/jsm/{renderers/nodes → nodes}/core/constants.js +0 -0
  326. package/examples/jsm/nodes/display/ColorSpaceNode.js +95 -0
  327. package/examples/jsm/nodes/display/FrontFacingNode.js +21 -0
  328. package/examples/jsm/nodes/display/NormalMapNode.js +86 -0
  329. package/examples/jsm/nodes/display/ToneMappingNode.js +51 -0
  330. package/examples/jsm/nodes/fog/FogNode.js +31 -0
  331. package/examples/jsm/nodes/fog/FogRangeNode.js +27 -0
  332. package/examples/jsm/nodes/functions/BSDF/BRDF_GGX.js +33 -0
  333. package/examples/jsm/nodes/functions/BSDF/BRDF_Lambert.js +9 -0
  334. package/examples/jsm/nodes/functions/BSDF/DFGApprox.js +27 -0
  335. package/examples/jsm/nodes/functions/BSDF/D_GGX.js +18 -0
  336. package/examples/jsm/nodes/functions/BSDF/F_Schlick.js +18 -0
  337. package/examples/jsm/nodes/functions/BSDF/V_GGX_SmithCorrelated.js +18 -0
  338. package/examples/jsm/nodes/functions/PhysicalLightingModel.js +94 -0
  339. package/examples/jsm/nodes/functions/light/getDistanceAttenuation.js +22 -0
  340. package/examples/jsm/nodes/functions/material/getGeometryRoughness.js +12 -0
  341. package/examples/jsm/nodes/functions/material/getRoughness.js +18 -0
  342. package/examples/jsm/nodes/gpgpu/ComputeNode.js +63 -0
  343. package/examples/jsm/nodes/lighting/AONode.js +25 -0
  344. package/examples/jsm/nodes/lighting/AnalyticLightNode.js +37 -0
  345. package/examples/jsm/nodes/lighting/EnvironmentNode.js +65 -0
  346. package/examples/jsm/nodes/lighting/HemisphereLightNode.js +50 -0
  347. package/examples/jsm/nodes/lighting/LightingContextNode.js +75 -0
  348. package/examples/jsm/nodes/lighting/LightingNode.js +19 -0
  349. package/examples/jsm/nodes/lighting/LightsNode.js +120 -0
  350. package/examples/jsm/nodes/lighting/PunctualLightNode.js +68 -0
  351. package/examples/jsm/nodes/loaders/NodeLoader.js +107 -0
  352. package/examples/jsm/nodes/loaders/NodeMaterialLoader.js +42 -0
  353. package/examples/jsm/nodes/loaders/NodeObjectLoader.js +70 -0
  354. package/examples/jsm/{renderers/nodes → nodes}/materials/LineBasicNodeMaterial.js +11 -4
  355. package/examples/jsm/nodes/materials/Materials.js +65 -0
  356. package/examples/jsm/{renderers/nodes → nodes}/materials/MeshBasicNodeMaterial.js +13 -4
  357. package/examples/jsm/nodes/materials/MeshStandardNodeMaterial.js +162 -22
  358. package/examples/jsm/nodes/materials/NodeMaterial.js +128 -113
  359. package/examples/jsm/{renderers/nodes → nodes}/materials/PointsNodeMaterial.js +13 -4
  360. package/examples/jsm/nodes/materials/SpriteNodeMaterial.js +84 -16
  361. package/examples/jsm/nodes/math/CondNode.js +26 -95
  362. package/examples/jsm/nodes/math/MathNode.js +164 -187
  363. package/examples/jsm/nodes/math/OperatorNode.js +161 -41
  364. package/examples/jsm/{renderers/nodes → nodes}/parsers/GLSLNodeFunction.js +2 -2
  365. package/examples/jsm/{renderers/nodes → nodes}/parsers/GLSLNodeParser.js +0 -0
  366. package/examples/jsm/{renderers/nodes → nodes}/parsers/WGSLNodeFunction.js +20 -5
  367. package/examples/jsm/{renderers/nodes → nodes}/parsers/WGSLNodeParser.js +0 -0
  368. package/examples/jsm/nodes/procedural/CheckerNode.js +16 -57
  369. package/examples/jsm/nodes/shadernode/ShaderNode.js +262 -0
  370. package/examples/jsm/nodes/shadernode/ShaderNodeBaseElements.js +274 -0
  371. package/examples/jsm/nodes/shadernode/ShaderNodeElements.js +106 -0
  372. package/examples/jsm/{renderers/nodes → nodes}/utils/ArrayElementNode.js +3 -3
  373. package/examples/jsm/{renderers/nodes → nodes}/utils/ConvertNode.js +11 -3
  374. package/examples/jsm/nodes/utils/JoinNode.js +17 -89
  375. package/examples/jsm/nodes/utils/MatcapUVNode.js +25 -0
  376. package/examples/jsm/nodes/utils/MaxMipLevelNode.js +32 -0
  377. package/examples/jsm/{renderers/nodes → nodes}/utils/OscNode.js +18 -2
  378. package/examples/jsm/{renderers/nodes → nodes}/utils/SplitNode.js +35 -4
  379. package/examples/jsm/{renderers/nodes → nodes}/utils/SpriteSheetUVNode.js +3 -3
  380. package/examples/jsm/nodes/utils/TimerNode.js +30 -65
  381. package/examples/jsm/objects/Lensflare.js +6 -6
  382. package/examples/jsm/objects/LightningStorm.js +4 -4
  383. package/examples/jsm/objects/MarchingCubes.js +5 -9
  384. package/examples/jsm/objects/Reflector.js +8 -19
  385. package/examples/jsm/objects/ReflectorForSSRPass.js +2 -14
  386. package/examples/jsm/objects/Refractor.js +8 -19
  387. package/examples/jsm/objects/ShadowMesh.js +2 -2
  388. package/examples/jsm/objects/Sky.js +2 -2
  389. package/examples/jsm/objects/Water.js +3 -18
  390. package/examples/jsm/objects/Water2.js +2 -2
  391. package/examples/jsm/offscreen/jank.js +5 -5
  392. package/examples/jsm/offscreen/offscreen.js +1 -1
  393. package/examples/jsm/offscreen/scene.js +11 -11
  394. package/examples/jsm/postprocessing/AdaptiveToneMappingPass.js +5 -7
  395. package/examples/jsm/postprocessing/AfterimagePass.js +0 -10
  396. package/examples/jsm/postprocessing/BloomPass.js +48 -20
  397. package/examples/jsm/postprocessing/DotScreenPass.js +1 -1
  398. package/examples/jsm/postprocessing/EffectComposer.js +1 -9
  399. package/examples/jsm/postprocessing/GlitchPass.js +7 -6
  400. package/examples/jsm/postprocessing/LUTPass.js +2 -2
  401. package/examples/jsm/postprocessing/OutlinePass.js +8 -15
  402. package/examples/jsm/postprocessing/SAOPass.js +2 -9
  403. package/examples/jsm/postprocessing/SMAAPass.js +2 -11
  404. package/examples/jsm/postprocessing/SSAARenderPass.js +1 -4
  405. package/examples/jsm/postprocessing/SSAOPass.js +13 -25
  406. package/examples/jsm/postprocessing/SSRPass.js +3 -9
  407. package/examples/jsm/postprocessing/SavePass.js +1 -3
  408. package/examples/jsm/postprocessing/TAARenderPass.js +4 -4
  409. package/examples/jsm/postprocessing/UnrealBloomPass.js +3 -8
  410. package/examples/jsm/renderers/CSS2DRenderer.js +21 -19
  411. package/examples/jsm/renderers/CSS3DRenderer.js +39 -34
  412. package/examples/jsm/renderers/Projector.js +101 -128
  413. package/examples/jsm/renderers/SVGRenderer.js +4 -4
  414. package/examples/jsm/renderers/webgl/nodes/SlotNode.js +1 -1
  415. package/examples/jsm/renderers/webgl/nodes/WebGLNodeBuilder.js +127 -23
  416. package/examples/jsm/renderers/webgl/nodes/WebGLNodes.js +14 -3
  417. package/examples/jsm/renderers/webgl/nodes/WebGLPhysicalContextNode.js +5 -5
  418. package/examples/jsm/renderers/webgpu/WebGPUAttributes.js +4 -4
  419. package/examples/jsm/renderers/webgpu/WebGPUBackground.js +16 -10
  420. package/examples/jsm/renderers/webgpu/WebGPUBindings.js +8 -7
  421. package/examples/jsm/renderers/webgpu/WebGPUBuffer.js +43 -0
  422. package/examples/jsm/renderers/webgpu/WebGPUComputePipelines.js +14 -7
  423. package/examples/jsm/renderers/webgpu/WebGPUInfo.js +1 -1
  424. package/examples/jsm/renderers/webgpu/WebGPUObjects.js +0 -6
  425. package/examples/jsm/renderers/webgpu/WebGPURenderPipeline.js +20 -20
  426. package/examples/jsm/renderers/webgpu/WebGPURenderPipelines.js +2 -1
  427. package/examples/jsm/renderers/webgpu/WebGPURenderStates.js +66 -0
  428. package/examples/jsm/renderers/webgpu/WebGPURenderer.js +49 -28
  429. package/examples/jsm/renderers/webgpu/WebGPUSampledTexture.js +14 -14
  430. package/examples/jsm/renderers/webgpu/WebGPUSampler.js +2 -2
  431. package/examples/jsm/renderers/webgpu/WebGPUStorageBuffer.js +5 -8
  432. package/examples/jsm/renderers/webgpu/WebGPUTextureUtils.js +23 -20
  433. package/examples/jsm/renderers/webgpu/WebGPUTextures.js +38 -20
  434. package/examples/jsm/renderers/webgpu/WebGPUUniform.js +14 -14
  435. package/examples/jsm/renderers/webgpu/WebGPUUniformBuffer.js +5 -32
  436. package/examples/jsm/renderers/webgpu/WebGPUUniformsGroup.js +2 -2
  437. package/examples/jsm/renderers/webgpu/constants.js +3 -2
  438. package/examples/jsm/renderers/webgpu/nodes/WebGPUNodeBuilder.js +276 -278
  439. package/examples/jsm/renderers/webgpu/nodes/WebGPUNodeSampledTexture.js +20 -2
  440. package/examples/jsm/renderers/webgpu/nodes/WebGPUNodes.js +24 -14
  441. package/examples/jsm/shaders/BlendShader.js +2 -1
  442. package/examples/jsm/shaders/CopyShader.js +4 -3
  443. package/examples/jsm/shaders/DigitalGlitch.js +3 -3
  444. package/examples/jsm/shaders/FXAAShader.js +219 -1051
  445. package/examples/jsm/shaders/FreiChenShader.js +1 -1
  446. package/examples/jsm/shaders/HorizontalBlurShader.js +1 -1
  447. package/examples/jsm/shaders/HorizontalTiltShiftShader.js +1 -1
  448. package/examples/jsm/shaders/MMDToonShader.js +0 -1
  449. package/examples/jsm/shaders/SSAOShader.js +1 -1
  450. package/examples/jsm/shaders/SSRShader.js +3 -3
  451. package/examples/jsm/shaders/ToneMapShader.js +1 -1
  452. package/examples/jsm/utils/BufferGeometryUtils.js +292 -22
  453. package/examples/jsm/utils/GPUStatsPanel.js +1 -1
  454. package/examples/jsm/utils/LDrawUtils.js +202 -0
  455. package/examples/jsm/utils/SceneUtils.js +76 -2
  456. package/examples/jsm/utils/SkeletonUtils.js +1 -1
  457. package/examples/jsm/utils/UVsDebug.js +23 -32
  458. package/examples/jsm/webxr/ARButton.js +14 -4
  459. package/examples/jsm/webxr/OculusHandPointerModel.js +3 -3
  460. package/examples/jsm/webxr/Text2D.js +1 -1
  461. package/examples/jsm/webxr/VRButton.js +39 -1
  462. package/examples/jsm/webxr/XREstimatedLight.js +2 -0
  463. package/examples/jsm/webxr/XRHandMeshModel.js +3 -7
  464. package/examples/jsm/webxr/XRHandModelFactory.js +3 -0
  465. package/package.json +53 -25
  466. package/src/Three.Legacy.js +41 -1912
  467. package/src/Three.js +6 -3
  468. package/src/animation/AnimationClip.js +1 -1
  469. package/src/animation/AnimationMixer.js +6 -3
  470. package/src/animation/AnimationObjectGroup.js +2 -2
  471. package/src/animation/PropertyBinding.js +8 -18
  472. package/src/audio/PositionalAudio.js +8 -0
  473. package/src/cameras/ArrayCamera.js +2 -3
  474. package/src/cameras/Camera.js +2 -2
  475. package/src/cameras/CubeCamera.js +8 -1
  476. package/src/cameras/OrthographicCamera.js +2 -2
  477. package/src/cameras/PerspectiveCamera.js +2 -2
  478. package/src/constants.js +8 -18
  479. package/src/core/BufferAttribute.js +7 -12
  480. package/src/core/BufferGeometry.js +6 -6
  481. package/src/core/GLBufferAttribute.js +2 -2
  482. package/src/core/InstancedBufferAttribute.js +2 -2
  483. package/src/core/InstancedBufferGeometry.js +2 -2
  484. package/src/core/InstancedInterleavedBuffer.js +2 -2
  485. package/src/core/InterleavedBuffer.js +2 -2
  486. package/src/core/InterleavedBufferAttribute.js +5 -11
  487. package/src/core/Object3D.js +6 -3
  488. package/src/core/Raycaster.js +2 -2
  489. package/src/extras/DataUtils.js +127 -40
  490. package/src/extras/Earcut.js +2 -2
  491. package/src/extras/ImageUtils.js +63 -0
  492. package/src/extras/PMREMGenerator.js +152 -174
  493. package/src/extras/core/CurvePath.js +3 -3
  494. package/src/extras/core/ShapePath.js +5 -6
  495. package/src/extras/curves/ArcCurve.js +2 -2
  496. package/src/extras/curves/CatmullRomCurve3.js +2 -2
  497. package/src/extras/curves/CubicBezierCurve.js +2 -2
  498. package/src/extras/curves/CubicBezierCurve3.js +2 -2
  499. package/src/extras/curves/EllipseCurve.js +2 -2
  500. package/src/extras/curves/LineCurve.js +2 -2
  501. package/src/extras/curves/LineCurve3.js +2 -1
  502. package/src/extras/curves/QuadraticBezierCurve.js +2 -2
  503. package/src/extras/curves/QuadraticBezierCurve3.js +2 -2
  504. package/src/extras/curves/SplineCurve.js +2 -2
  505. package/src/geometries/CapsuleGeometry.js +33 -0
  506. package/src/geometries/ExtrudeGeometry.js +2 -0
  507. package/src/geometries/Geometries.js +1 -0
  508. package/src/geometries/LatheGeometry.js +1 -1
  509. package/src/geometries/TorusKnotGeometry.js +2 -2
  510. package/src/geometries/WireframeGeometry.js +3 -2
  511. package/src/helpers/BoxHelper.js +2 -2
  512. package/src/helpers/PointLightHelper.js +1 -1
  513. package/src/helpers/SkeletonHelper.js +3 -2
  514. package/src/lights/AmbientLight.js +2 -2
  515. package/src/lights/AmbientLightProbe.js +2 -2
  516. package/src/lights/DirectionalLight.js +2 -2
  517. package/src/lights/DirectionalLightShadow.js +2 -2
  518. package/src/lights/HemisphereLight.js +4 -4
  519. package/src/lights/HemisphereLightProbe.js +2 -2
  520. package/src/lights/Light.js +4 -4
  521. package/src/lights/LightProbe.js +2 -2
  522. package/src/lights/PointLight.js +4 -4
  523. package/src/lights/PointLightShadow.js +2 -2
  524. package/src/lights/RectAreaLight.js +2 -2
  525. package/src/lights/SpotLight.js +4 -4
  526. package/src/lights/SpotLightShadow.js +2 -2
  527. package/src/loaders/FileLoader.js +23 -4
  528. package/src/loaders/ImageBitmapLoader.js +2 -2
  529. package/src/loaders/ImageLoader.js +1 -1
  530. package/src/loaders/LoaderUtils.js +1 -1
  531. package/src/loaders/MaterialLoader.js +8 -3
  532. package/src/loaders/ObjectLoader.js +23 -25
  533. package/src/materials/LineBasicMaterial.js +6 -13
  534. package/src/materials/LineDashedMaterial.js +2 -15
  535. package/src/materials/Material.js +49 -15
  536. package/src/materials/Materials.js +66 -18
  537. package/src/materials/MeshBasicMaterial.js +6 -31
  538. package/src/materials/MeshDepthMaterial.js +2 -22
  539. package/src/materials/MeshDistanceMaterial.js +2 -22
  540. package/src/materials/MeshLambertMaterial.js +6 -34
  541. package/src/materials/MeshMatcapMaterial.js +6 -28
  542. package/src/materials/MeshNormalMaterial.js +2 -26
  543. package/src/materials/MeshPhongMaterial.js +6 -48
  544. package/src/materials/MeshPhysicalMaterial.js +32 -35
  545. package/src/materials/MeshStandardMaterial.js +6 -54
  546. package/src/materials/MeshToonMaterial.js +6 -38
  547. package/src/materials/PointsMaterial.js +6 -15
  548. package/src/materials/RawShaderMaterial.js +2 -2
  549. package/src/materials/ShaderMaterial.js +3 -17
  550. package/src/materials/ShadowMaterial.js +6 -8
  551. package/src/materials/SpriteMaterial.js +6 -12
  552. package/src/math/Box2.js +2 -2
  553. package/src/math/Box3.js +29 -15
  554. package/src/math/Color.js +85 -36
  555. package/src/math/ColorManagement.js +74 -0
  556. package/src/math/Euler.js +16 -14
  557. package/src/math/Interpolant.js +4 -9
  558. package/src/math/MathUtils.js +76 -7
  559. package/src/math/Matrix3.js +2 -2
  560. package/src/math/Matrix4.js +2 -3
  561. package/src/math/Plane.js +2 -2
  562. package/src/math/Quaternion.js +12 -3
  563. package/src/math/Spherical.js +1 -1
  564. package/src/math/SphericalHarmonics3.js +2 -2
  565. package/src/math/Vector2.js +2 -2
  566. package/src/math/Vector3.js +12 -3
  567. package/src/math/Vector4.js +2 -2
  568. package/src/math/interpolants/DiscreteInterpolant.js +1 -1
  569. package/src/objects/Bone.js +2 -2
  570. package/src/objects/Group.js +2 -2
  571. package/src/objects/InstancedMesh.js +4 -4
  572. package/src/objects/Line.js +76 -107
  573. package/src/objects/LineLoop.js +2 -2
  574. package/src/objects/LineSegments.js +15 -23
  575. package/src/objects/Mesh.js +82 -104
  576. package/src/objects/Points.js +32 -54
  577. package/src/objects/SkinnedMesh.js +5 -8
  578. package/src/objects/Sprite.js +4 -4
  579. package/src/renderers/WebGL3DRenderTarget.js +22 -0
  580. package/src/renderers/WebGLArrayRenderTarget.js +22 -0
  581. package/src/renderers/WebGLCubeRenderTarget.js +7 -16
  582. package/src/renderers/WebGLMultipleRenderTargets.js +8 -5
  583. package/src/renderers/WebGLRenderTarget.js +19 -18
  584. package/src/renderers/WebGLRenderer.js +112 -133
  585. package/src/renderers/shaders/ShaderChunk/bsdfs.glsl.js +46 -0
  586. package/src/renderers/shaders/ShaderChunk/common.glsl.js +1 -0
  587. package/src/renderers/shaders/ShaderChunk/cube_uv_reflection_fragment.glsl.js +12 -13
  588. package/src/renderers/shaders/ShaderChunk/dithering_pars_fragment.glsl.js +1 -1
  589. package/src/renderers/shaders/ShaderChunk/emissivemap_fragment.glsl.js +0 -2
  590. package/src/renderers/shaders/ShaderChunk/encodings_pars_fragment.glsl.js +0 -4
  591. package/src/renderers/shaders/ShaderChunk/envmap_fragment.glsl.js +0 -2
  592. package/src/renderers/shaders/ShaderChunk/envmap_physical_pars_fragment.glsl.js +3 -19
  593. package/src/renderers/shaders/ShaderChunk/iridescence_fragment.glsl.js +107 -0
  594. package/src/renderers/shaders/ShaderChunk/iridescence_pars_fragment.glsl.js +14 -0
  595. package/src/renderers/shaders/ShaderChunk/lightmap_fragment.glsl.js +1 -7
  596. package/src/renderers/shaders/ShaderChunk/lights_fragment_begin.glsl.js +25 -0
  597. package/src/renderers/shaders/ShaderChunk/lights_fragment_maps.glsl.js +1 -7
  598. package/src/renderers/shaders/ShaderChunk/lights_physical_fragment.glsl.js +25 -2
  599. package/src/renderers/shaders/ShaderChunk/lights_physical_pars_fragment.glsl.js +43 -5
  600. package/src/renderers/shaders/ShaderChunk/map_fragment.glsl.js +10 -3
  601. package/src/renderers/shaders/ShaderChunk/map_particle_fragment.glsl.js +1 -2
  602. package/src/renderers/shaders/ShaderChunk/morphcolor_vertex.glsl.js +24 -0
  603. package/src/renderers/shaders/ShaderChunk/morphnormal_vertex.glsl.js +1 -1
  604. package/src/renderers/shaders/ShaderChunk/morphtarget_pars_vertex.glsl.js +7 -7
  605. package/src/renderers/shaders/ShaderChunk/morphtarget_vertex.glsl.js +1 -9
  606. package/src/renderers/shaders/ShaderChunk/skinning_pars_vertex.glsl.js +16 -31
  607. package/src/renderers/shaders/ShaderChunk/transmission_pars_fragment.glsl.js +9 -8
  608. package/src/renderers/shaders/ShaderChunk.js +6 -0
  609. package/src/renderers/shaders/ShaderLib/background.glsl.js +8 -2
  610. package/src/renderers/shaders/ShaderLib/equirect.glsl.js +1 -3
  611. package/src/renderers/shaders/ShaderLib/linedashed.glsl.js +1 -0
  612. package/src/renderers/shaders/ShaderLib/meshbasic.glsl.js +3 -2
  613. package/src/renderers/shaders/ShaderLib/meshlambert.glsl.js +1 -0
  614. package/src/renderers/shaders/ShaderLib/meshmatcap.glsl.js +2 -2
  615. package/src/renderers/shaders/ShaderLib/meshnormal.glsl.js +6 -0
  616. package/src/renderers/shaders/ShaderLib/meshphong.glsl.js +1 -0
  617. package/src/renderers/shaders/ShaderLib/meshphysical.glsl.js +10 -0
  618. package/src/renderers/shaders/ShaderLib/meshtoon.glsl.js +1 -0
  619. package/src/renderers/shaders/ShaderLib/points.glsl.js +1 -0
  620. package/src/renderers/shaders/ShaderLib.js +8 -2
  621. package/src/renderers/shaders/UniformsLib.js +2 -2
  622. package/src/renderers/webgl/WebGLAttributes.js +6 -6
  623. package/src/renderers/webgl/WebGLBackground.js +5 -2
  624. package/src/renderers/webgl/WebGLBindingStates.js +57 -29
  625. package/src/renderers/webgl/WebGLCubeMaps.js +0 -4
  626. package/src/renderers/webgl/WebGLCubeUVMaps.js +29 -16
  627. package/src/renderers/webgl/WebGLGeometries.js +2 -2
  628. package/src/renderers/webgl/WebGLLights.js +0 -1
  629. package/src/renderers/webgl/WebGLMaterials.js +72 -187
  630. package/src/renderers/webgl/WebGLMorphtargets.js +59 -23
  631. package/src/renderers/webgl/WebGLProgram.js +56 -30
  632. package/src/renderers/webgl/WebGLPrograms.js +80 -130
  633. package/src/renderers/webgl/WebGLShaderCache.js +4 -3
  634. package/src/renderers/webgl/WebGLShadowMap.js +6 -8
  635. package/src/renderers/webgl/WebGLState.js +83 -2
  636. package/src/renderers/webgl/WebGLTextures.js +618 -375
  637. package/src/renderers/webgl/WebGLUniforms.js +79 -80
  638. package/src/renderers/webgl/WebGLUtils.js +97 -30
  639. package/src/renderers/webxr/WebXRManager.js +64 -47
  640. package/src/scenes/Fog.js +2 -2
  641. package/src/scenes/FogExp2.js +2 -2
  642. package/src/scenes/Scene.js +2 -2
  643. package/src/textures/CanvasTexture.js +2 -2
  644. package/src/textures/CompressedTexture.js +2 -2
  645. package/src/textures/CubeTexture.js +2 -2
  646. package/src/textures/{DataTexture3D.js → Data3DTexture.js} +4 -4
  647. package/src/textures/{DataTexture2DArray.js → DataArrayTexture.js} +4 -4
  648. package/src/textures/DataTexture.js +2 -5
  649. package/src/textures/DepthTexture.js +5 -5
  650. package/src/textures/FramebufferTexture.js +2 -2
  651. package/src/textures/Source.js +123 -0
  652. package/src/textures/Texture.js +29 -98
  653. package/src/textures/VideoTexture.js +2 -4
  654. package/src/utils.js +15 -1
  655. package/examples/js/WebGL.js +0 -90
  656. package/examples/js/csm/Frustum.js +0 -133
  657. package/examples/js/deprecated/Geometry.js +0 -1656
  658. package/examples/js/loaders/VRMLoader.js +0 -72
  659. package/examples/js/objects/ReflectorRTT.js +0 -16
  660. package/examples/js/postprocessing/SSRrPass.js +0 -494
  661. package/examples/js/shaders/SSRrShader.js +0 -336
  662. package/examples/js/utils/RoughnessMipmapper.js +0 -268
  663. package/examples/jsm/deprecated/Geometry.js +0 -1869
  664. package/examples/jsm/libs/opentype.module.min.js +0 -119
  665. package/examples/jsm/loaders/NodeMaterialLoader.js +0 -263
  666. package/examples/jsm/loaders/VRMLoader.js +0 -78
  667. package/examples/jsm/nodes/accessors/ColorsNode.js +0 -56
  668. package/examples/jsm/nodes/accessors/LightNode.js +0 -60
  669. package/examples/jsm/nodes/accessors/ResolutionNode.js +0 -64
  670. package/examples/jsm/nodes/accessors/ScreenUVNode.js +0 -64
  671. package/examples/jsm/nodes/core/NodeLib.js +0 -64
  672. package/examples/jsm/nodes/core/StructNode.js +0 -106
  673. package/examples/jsm/nodes/effects/BlurNode.js +0 -171
  674. package/examples/jsm/nodes/effects/ColorAdjustmentNode.js +0 -140
  675. package/examples/jsm/nodes/effects/LuminanceNode.js +0 -75
  676. package/examples/jsm/nodes/inputs/BoolNode.js +0 -51
  677. package/examples/jsm/nodes/inputs/ColorNode.js +0 -58
  678. package/examples/jsm/nodes/inputs/CubeTextureNode.js +0 -108
  679. package/examples/jsm/nodes/inputs/FloatNode.js +0 -51
  680. package/examples/jsm/nodes/inputs/IntNode.js +0 -51
  681. package/examples/jsm/nodes/inputs/Matrix3Node.js +0 -63
  682. package/examples/jsm/nodes/inputs/Matrix4Node.js +0 -63
  683. package/examples/jsm/nodes/inputs/PropertyNode.js +0 -47
  684. package/examples/jsm/nodes/inputs/RTTNode.js +0 -157
  685. package/examples/jsm/nodes/inputs/ReflectorNode.js +0 -90
  686. package/examples/jsm/nodes/inputs/ScreenNode.js +0 -28
  687. package/examples/jsm/nodes/inputs/TextureNode.js +0 -116
  688. package/examples/jsm/nodes/inputs/Vector2Node.js +0 -57
  689. package/examples/jsm/nodes/inputs/Vector3Node.js +0 -58
  690. package/examples/jsm/nodes/inputs/Vector4Node.js +0 -59
  691. package/examples/jsm/nodes/materials/BasicNodeMaterial.js +0 -26
  692. package/examples/jsm/nodes/materials/PhongNodeMaterial.js +0 -36
  693. package/examples/jsm/nodes/materials/StandardNodeMaterial.js +0 -40
  694. package/examples/jsm/nodes/materials/nodes/BasicNode.js +0 -154
  695. package/examples/jsm/nodes/materials/nodes/MeshStandardNode.js +0 -116
  696. package/examples/jsm/nodes/materials/nodes/PhongNode.js +0 -409
  697. package/examples/jsm/nodes/materials/nodes/RawNode.js +0 -62
  698. package/examples/jsm/nodes/materials/nodes/SpriteNode.js +0 -236
  699. package/examples/jsm/nodes/materials/nodes/StandardNode.js +0 -609
  700. package/examples/jsm/nodes/misc/BumpMapNode.js +0 -163
  701. package/examples/jsm/nodes/misc/NormalMapNode.js +0 -138
  702. package/examples/jsm/nodes/misc/TextureCubeNode.js +0 -86
  703. package/examples/jsm/nodes/misc/TextureCubeUVNode.js +0 -280
  704. package/examples/jsm/nodes/postprocessing/NodePass.js +0 -93
  705. package/examples/jsm/nodes/postprocessing/NodePostProcessing.js +0 -148
  706. package/examples/jsm/nodes/procedural/Fractal3DNode.js +0 -104
  707. package/examples/jsm/nodes/procedural/Noise2DNode.js +0 -149
  708. package/examples/jsm/nodes/procedural/Noise3DNode.js +0 -141
  709. package/examples/jsm/nodes/utils/BypassNode.js +0 -85
  710. package/examples/jsm/nodes/utils/ColorSpaceNode.js +0 -154
  711. package/examples/jsm/nodes/utils/MaxMIPLevelNode.js +0 -56
  712. package/examples/jsm/nodes/utils/RemapNode.js +0 -155
  713. package/examples/jsm/nodes/utils/SpecularMIPLevelNode.js +0 -101
  714. package/examples/jsm/nodes/utils/SubSlotNode.js +0 -77
  715. package/examples/jsm/nodes/utils/SwitchNode.js +0 -103
  716. package/examples/jsm/nodes/utils/UVTransformNode.js +0 -66
  717. package/examples/jsm/nodes/utils/VelocityNode.js +0 -170
  718. package/examples/jsm/objects/ReflectorRTT.js +0 -15
  719. package/examples/jsm/postprocessing/SSRrPass.js +0 -579
  720. package/examples/jsm/renderers/nodes/Nodes.js +0 -164
  721. package/examples/jsm/renderers/nodes/ShaderNode.js +0 -346
  722. package/examples/jsm/renderers/nodes/accessors/CameraNode.js +0 -68
  723. package/examples/jsm/renderers/nodes/accessors/NormalNode.js +0 -63
  724. package/examples/jsm/renderers/nodes/accessors/PositionNode.js +0 -67
  725. package/examples/jsm/renderers/nodes/accessors/ReferenceNode.js +0 -91
  726. package/examples/jsm/renderers/nodes/accessors/SkinningNode.js +0 -107
  727. package/examples/jsm/renderers/nodes/accessors/UVNode.js +0 -25
  728. package/examples/jsm/renderers/nodes/core/ArrayInputNode.js +0 -23
  729. package/examples/jsm/renderers/nodes/core/AttributeNode.js +0 -54
  730. package/examples/jsm/renderers/nodes/core/ExpressionNode.js +0 -32
  731. package/examples/jsm/renderers/nodes/core/FunctionCallNode.js +0 -67
  732. package/examples/jsm/renderers/nodes/core/FunctionNode.js +0 -87
  733. package/examples/jsm/renderers/nodes/core/InputNode.js +0 -66
  734. package/examples/jsm/renderers/nodes/core/Node.js +0 -104
  735. package/examples/jsm/renderers/nodes/core/NodeBuilder.js +0 -633
  736. package/examples/jsm/renderers/nodes/core/NodeFrame.js +0 -59
  737. package/examples/jsm/renderers/nodes/core/NodeUniform.js +0 -28
  738. package/examples/jsm/renderers/nodes/core/TempNode.js +0 -43
  739. package/examples/jsm/renderers/nodes/core/VarNode.js +0 -51
  740. package/examples/jsm/renderers/nodes/display/ColorSpaceNode.js +0 -133
  741. package/examples/jsm/renderers/nodes/display/NormalMapNode.js +0 -87
  742. package/examples/jsm/renderers/nodes/functions/BSDFs.js +0 -122
  743. package/examples/jsm/renderers/nodes/functions/PhysicalMaterialFunctions.js +0 -27
  744. package/examples/jsm/renderers/nodes/inputs/BufferNode.js +0 -25
  745. package/examples/jsm/renderers/nodes/inputs/ColorNode.js +0 -18
  746. package/examples/jsm/renderers/nodes/inputs/FloatNode.js +0 -17
  747. package/examples/jsm/renderers/nodes/inputs/IntNode.js +0 -17
  748. package/examples/jsm/renderers/nodes/inputs/Matrix3Node.js +0 -18
  749. package/examples/jsm/renderers/nodes/inputs/Matrix4Node.js +0 -18
  750. package/examples/jsm/renderers/nodes/inputs/TextureNode.js +0 -79
  751. package/examples/jsm/renderers/nodes/inputs/Vector2Node.js +0 -18
  752. package/examples/jsm/renderers/nodes/inputs/Vector3Node.js +0 -18
  753. package/examples/jsm/renderers/nodes/inputs/Vector4Node.js +0 -18
  754. package/examples/jsm/renderers/nodes/lights/LightContextNode.js +0 -59
  755. package/examples/jsm/renderers/nodes/lights/LightNode.js +0 -79
  756. package/examples/jsm/renderers/nodes/lights/LightsNode.js +0 -44
  757. package/examples/jsm/renderers/nodes/materials/Materials.js +0 -11
  758. package/examples/jsm/renderers/nodes/materials/MeshStandardNodeMaterial.js +0 -63
  759. package/examples/jsm/renderers/nodes/math/CondNode.js +0 -60
  760. package/examples/jsm/renderers/nodes/math/MathNode.js +0 -241
  761. package/examples/jsm/renderers/nodes/math/OperatorNode.js +0 -174
  762. package/examples/jsm/renderers/nodes/procedural/CheckerNode.js +0 -36
  763. package/examples/jsm/renderers/nodes/utils/JoinNode.js +0 -42
  764. package/examples/jsm/renderers/nodes/utils/TimerNode.js +0 -47
  765. package/examples/jsm/renderers/webgpu/nodes/WebGPUNodeUniformsGroup.js +0 -20
  766. package/examples/jsm/shaders/SSRrShader.js +0 -307
  767. package/examples/jsm/utils/RoughnessMipmapper.js +0 -288
  768. package/src/renderers/WebGLMultisampleRenderTarget.js +0 -33
@@ -17,6 +17,8 @@
17
17
  const FILE_LOCATION_TRY_RELATIVE = 4;
18
18
  const FILE_LOCATION_TRY_ABSOLUTE = 5;
19
19
  const FILE_LOCATION_NOT_FOUND = 6;
20
+ const MAIN_COLOUR_CODE = '16';
21
+ const MAIN_EDGE_COLOUR_CODE = '24';
20
22
 
21
23
  const _tempVec0 = new THREE.Vector3();
22
24
 
@@ -142,16 +144,56 @@
142
144
 
143
145
  }
144
146
 
145
- function smoothNormals( faces, lineSegments ) {
147
+ class ConditionalLineSegments extends THREE.LineSegments {
148
+
149
+ constructor( geometry, material ) {
150
+
151
+ super( geometry, material );
152
+ this.isConditionalLine = true;
153
+
154
+ }
155
+
156
+ }
157
+
158
+ function generateFaceNormals( faces ) {
159
+
160
+ for ( let i = 0, l = faces.length; i < l; i ++ ) {
161
+
162
+ const face = faces[ i ];
163
+ const vertices = face.vertices;
164
+ const v0 = vertices[ 0 ];
165
+ const v1 = vertices[ 1 ];
166
+ const v2 = vertices[ 2 ];
167
+
168
+ _tempVec0.subVectors( v1, v0 );
169
+
170
+ _tempVec1.subVectors( v2, v1 );
171
+
172
+ face.faceNormal = new THREE.Vector3().crossVectors( _tempVec0, _tempVec1 ).normalize();
173
+
174
+ }
175
+
176
+ }
177
+
178
+ const _ray = new THREE.Ray();
179
+
180
+ function smoothNormals( faces, lineSegments, checkSubSegments = false ) {
181
+
182
+ // NOTE: 1e2 is pretty coarse but was chosen to quantize the resulting value because
183
+ // it allows edges to be smoothed as expected (see minifig arms).
184
+ // --
185
+ // And the vector values are initialize multiplied by 1 + 1e-10 to account for floating
186
+ // point errors on vertices along quantization boundaries. Ie after matrix multiplication
187
+ // vertices that should be merged might be set to "1.7" and "1.6999..." meaning they won't
188
+ // get merged. This added epsilon attempts to push these error values to the same quantized
189
+ // value for the sake of hashing. See "AT-ST mini" dishes. See mrdoob/three#23169.
190
+ const hashMultiplier = ( 1 + 1e-10 ) * 1e2;
146
191
 
147
192
  function hashVertex( v ) {
148
193
 
149
- // NOTE: 1e2 is pretty coarse but was chosen because it allows edges
150
- // to be smoothed as expected (see minifig arms). The errors between edges
151
- // could be due to matrix multiplication.
152
- const x = ~ ~ ( v.x * 1e2 );
153
- const y = ~ ~ ( v.y * 1e2 );
154
- const z = ~ ~ ( v.z * 1e2 );
194
+ const x = ~ ~ ( v.x * hashMultiplier );
195
+ const y = ~ ~ ( v.y * hashMultiplier );
196
+ const z = ~ ~ ( v.z * hashMultiplier );
155
197
  return `${x},${y},${z}`;
156
198
 
157
199
  }
@@ -160,9 +202,27 @@
160
202
 
161
203
  return `${hashVertex( v0 )}_${hashVertex( v1 )}`;
162
204
 
205
+ } // converts the two vertices to a ray with a normalized direction and origin of 0, 0, 0 projected
206
+ // onto the original line.
207
+
208
+
209
+ function toNormalizedRay( v0, v1, targetRay ) {
210
+
211
+ targetRay.direction.subVectors( v1, v0 ).normalize();
212
+ const scalar = v0.dot( targetRay.direction );
213
+ targetRay.origin.copy( v0 ).addScaledVector( targetRay.direction, - scalar );
214
+ return targetRay;
215
+
216
+ }
217
+
218
+ function hashRay( ray ) {
219
+
220
+ return hashEdge( ray.origin, ray.direction );
221
+
163
222
  }
164
223
 
165
224
  const hardEdges = new Set();
225
+ const hardEdgeRays = new Map();
166
226
  const halfEdgeList = {};
167
227
  const normals = []; // Save the list of hard edges by hash
168
228
 
@@ -173,7 +233,43 @@
173
233
  const v0 = vertices[ 0 ];
174
234
  const v1 = vertices[ 1 ];
175
235
  hardEdges.add( hashEdge( v0, v1 ) );
176
- hardEdges.add( hashEdge( v1, v0 ) );
236
+ hardEdges.add( hashEdge( v1, v0 ) ); // only generate the hard edge ray map if we're checking subsegments because it's more expensive to check
237
+ // and requires more memory.
238
+
239
+ if ( checkSubSegments ) {
240
+
241
+ // add both ray directions to the map
242
+ const ray = toNormalizedRay( v0, v1, new THREE.Ray() );
243
+ const rh1 = hashRay( ray );
244
+
245
+ if ( ! hardEdgeRays.has( rh1 ) ) {
246
+
247
+ toNormalizedRay( v1, v0, ray );
248
+ const rh2 = hashRay( ray );
249
+ const info = {
250
+ ray,
251
+ distances: []
252
+ };
253
+ hardEdgeRays.set( rh1, info );
254
+ hardEdgeRays.set( rh2, info );
255
+
256
+ } // store both segments ends in min, max order in the distances array to check if a face edge is a
257
+ // subsegment later.
258
+
259
+
260
+ const info = hardEdgeRays.get( rh1 );
261
+ let d0 = info.ray.direction.dot( v0 );
262
+ let d1 = info.ray.direction.dot( v1 );
263
+
264
+ if ( d0 > d1 ) {
265
+
266
+ [ d0, d1 ] = [ d1, d0 ];
267
+
268
+ }
269
+
270
+ info.distances.push( d0, d1 );
271
+
272
+ }
177
273
 
178
274
  } // track the half edges associated with each triangle
179
275
 
@@ -192,7 +288,58 @@
192
288
  const v1 = vertices[ next ];
193
289
  const hash = hashEdge( v0, v1 ); // don't add the triangle if the edge is supposed to be hard
194
290
 
195
- if ( hardEdges.has( hash ) ) continue;
291
+ if ( hardEdges.has( hash ) ) {
292
+
293
+ continue;
294
+
295
+ } // if checking subsegments then check to see if this edge lies on a hard edge ray and whether its within any ray bounds
296
+
297
+
298
+ if ( checkSubSegments ) {
299
+
300
+ toNormalizedRay( v0, v1, _ray );
301
+ const rayHash = hashRay( _ray );
302
+
303
+ if ( hardEdgeRays.has( rayHash ) ) {
304
+
305
+ const info = hardEdgeRays.get( rayHash );
306
+ const {
307
+ ray,
308
+ distances
309
+ } = info;
310
+ let d0 = ray.direction.dot( v0 );
311
+ let d1 = ray.direction.dot( v1 );
312
+
313
+ if ( d0 > d1 ) {
314
+
315
+ [ d0, d1 ] = [ d1, d0 ];
316
+
317
+ } // return early if the face edge is found to be a subsegment of a line edge meaning the edge will have "hard" normals
318
+
319
+
320
+ let found = false;
321
+
322
+ for ( let i = 0, l = distances.length; i < l; i += 2 ) {
323
+
324
+ if ( d0 >= distances[ i ] && d1 <= distances[ i + 1 ] ) {
325
+
326
+ found = true;
327
+ break;
328
+
329
+ }
330
+
331
+ }
332
+
333
+ if ( found ) {
334
+
335
+ continue;
336
+
337
+ }
338
+
339
+ }
340
+
341
+ }
342
+
196
343
  const info = {
197
344
  index: index,
198
345
  tri: tri
@@ -363,13 +510,7 @@
363
510
 
364
511
  function isPartType( type ) {
365
512
 
366
- return type === 'Part';
367
-
368
- }
369
-
370
- function isModelType( type ) {
371
-
372
- return type === 'Model' || type === 'Unofficial_Model';
513
+ return type === 'Part' || type === 'Unofficial_Part';
373
514
 
374
515
  }
375
516
 
@@ -433,6 +574,12 @@
433
574
 
434
575
  }
435
576
 
577
+ getVector() {
578
+
579
+ return new THREE.Vector3( parseFloat( this.getToken() ), parseFloat( this.getToken() ), parseFloat( this.getToken() ) );
580
+
581
+ }
582
+
436
583
  getRemainingString() {
437
584
 
438
585
  return this.line.substring( this.currentCharIndex, this.lineLength );
@@ -457,1469 +604,1702 @@
457
604
 
458
605
  }
459
606
 
460
- }
607
+ } // Fetches and parses an intermediate representation of LDraw parts files.
608
+
461
609
 
462
- class LDrawFileCache {
610
+ class LDrawParsedCache {
463
611
 
464
612
  constructor( loader ) {
465
613
 
466
- this.cache = {};
467
614
  this.loader = loader;
615
+ this._cache = {};
468
616
 
469
617
  }
470
618
 
471
- setData( key, contents ) {
619
+ cloneResult( original ) {
472
620
 
473
- this.cache[ key.toLowerCase() ] = contents;
621
+ const result = {}; // vertices are transformed and normals computed before being converted to geometry
622
+ // so these pieces must be cloned.
474
623
 
475
- }
624
+ result.faces = original.faces.map( face => {
476
625
 
477
- async loadData( fileName ) {
626
+ return {
627
+ colorCode: face.colorCode,
628
+ material: face.material,
629
+ vertices: face.vertices.map( v => v.clone() ),
630
+ normals: face.normals.map( () => null ),
631
+ faceNormal: null
632
+ };
478
633
 
479
- const key = fileName.toLowerCase();
634
+ } );
635
+ result.conditionalSegments = original.conditionalSegments.map( face => {
480
636
 
481
- if ( key in this.cache ) {
637
+ return {
638
+ colorCode: face.colorCode,
639
+ material: face.material,
640
+ vertices: face.vertices.map( v => v.clone() ),
641
+ controlPoints: face.controlPoints.map( v => v.clone() )
642
+ };
482
643
 
483
- return this.cache[ key ];
644
+ } );
645
+ result.lineSegments = original.lineSegments.map( face => {
484
646
 
485
- }
647
+ return {
648
+ colorCode: face.colorCode,
649
+ material: face.material,
650
+ vertices: face.vertices.map( v => v.clone() )
651
+ };
486
652
 
487
- this.cache[ fileName ] = new Promise( async ( resolve, reject ) => {
653
+ } ); // none if this is subsequently modified
488
654
 
489
- let triedLowerCase = false;
490
- let locationState = FILE_LOCATION_AS_IS;
655
+ result.type = original.type;
656
+ result.category = original.category;
657
+ result.keywords = original.keywords;
658
+ result.subobjects = original.subobjects;
659
+ result.totalFaces = original.totalFaces;
660
+ result.startingConstructionStep = original.startingConstructionStep;
661
+ result.materials = original.materials;
662
+ result.group = null;
663
+ return result;
491
664
 
492
- while ( locationState !== FILE_LOCATION_NOT_FOUND ) {
665
+ }
666
+
667
+ async fetchData( fileName ) {
493
668
 
494
- let subobjectURL = fileName;
669
+ let triedLowerCase = false;
670
+ let locationState = FILE_LOCATION_AS_IS;
495
671
 
496
- switch ( locationState ) {
672
+ while ( locationState !== FILE_LOCATION_NOT_FOUND ) {
497
673
 
498
- case FILE_LOCATION_AS_IS:
499
- locationState = locationState + 1;
500
- break;
674
+ let subobjectURL = fileName;
501
675
 
502
- case FILE_LOCATION_TRY_PARTS:
503
- subobjectURL = 'parts/' + subobjectURL;
504
- locationState = locationState + 1;
505
- break;
676
+ switch ( locationState ) {
677
+
678
+ case FILE_LOCATION_AS_IS:
679
+ locationState = locationState + 1;
680
+ break;
506
681
 
507
- case FILE_LOCATION_TRY_P:
508
- subobjectURL = 'p/' + subobjectURL;
509
- locationState = locationState + 1;
510
- break;
682
+ case FILE_LOCATION_TRY_PARTS:
683
+ subobjectURL = 'parts/' + subobjectURL;
684
+ locationState = locationState + 1;
685
+ break;
511
686
 
512
- case FILE_LOCATION_TRY_MODELS:
513
- subobjectURL = 'models/' + subobjectURL;
514
- locationState = locationState + 1;
515
- break;
687
+ case FILE_LOCATION_TRY_P:
688
+ subobjectURL = 'p/' + subobjectURL;
689
+ locationState = locationState + 1;
690
+ break;
516
691
 
517
- case FILE_LOCATION_TRY_RELATIVE:
518
- subobjectURL = fileName.substring( 0, fileName.lastIndexOf( '/' ) + 1 ) + subobjectURL;
519
- locationState = locationState + 1;
520
- break;
692
+ case FILE_LOCATION_TRY_MODELS:
693
+ subobjectURL = 'models/' + subobjectURL;
694
+ locationState = locationState + 1;
695
+ break;
521
696
 
522
- case FILE_LOCATION_TRY_ABSOLUTE:
523
- if ( triedLowerCase ) {
697
+ case FILE_LOCATION_TRY_RELATIVE:
698
+ subobjectURL = fileName.substring( 0, fileName.lastIndexOf( '/' ) + 1 ) + subobjectURL;
699
+ locationState = locationState + 1;
700
+ break;
524
701
 
525
- // Try absolute path
526
- locationState = FILE_LOCATION_NOT_FOUND;
702
+ case FILE_LOCATION_TRY_ABSOLUTE:
703
+ if ( triedLowerCase ) {
527
704
 
528
- } else {
705
+ // Try absolute path
706
+ locationState = FILE_LOCATION_NOT_FOUND;
529
707
 
530
- // Next attempt is lower case
531
- fileName = fileName.toLowerCase();
532
- subobjectURL = fileName;
533
- triedLowerCase = true;
534
- locationState = FILE_LOCATION_AS_IS;
708
+ } else {
535
709
 
536
- }
710
+ // Next attempt is lower case
711
+ fileName = fileName.toLowerCase();
712
+ subobjectURL = fileName;
713
+ triedLowerCase = true;
714
+ locationState = FILE_LOCATION_AS_IS;
537
715
 
538
- break;
716
+ }
539
717
 
540
- }
718
+ break;
541
719
 
542
- const loader = this.loader;
543
- const fileLoader = new THREE.FileLoader( loader.manager );
544
- fileLoader.setPath( loader.partsLibraryPath );
545
- fileLoader.setRequestHeader( loader.requestHeader );
546
- fileLoader.setWithCredentials( loader.withCredentials );
720
+ }
547
721
 
548
- try {
722
+ const loader = this.loader;
723
+ const fileLoader = new THREE.FileLoader( loader.manager );
724
+ fileLoader.setPath( loader.partsLibraryPath );
725
+ fileLoader.setRequestHeader( loader.requestHeader );
726
+ fileLoader.setWithCredentials( loader.withCredentials );
549
727
 
550
- const text = await fileLoader.loadAsync( subobjectURL );
551
- this.setData( fileName, text );
552
- resolve( text );
553
- return;
728
+ try {
554
729
 
555
- } catch {
730
+ const text = await fileLoader.loadAsync( subobjectURL );
731
+ return text;
556
732
 
557
- continue;
733
+ } catch {
558
734
 
559
- }
735
+ continue;
560
736
 
561
737
  }
562
738
 
563
- reject();
739
+ }
564
740
 
565
- } );
566
- return this.cache[ fileName ];
741
+ throw new Error( 'LDrawLoader: Subobject "' + fileName + '" could not be loaded.' );
567
742
 
568
743
  }
569
744
 
570
- }
745
+ parse( text, fileName = null ) {
571
746
 
572
- function sortByMaterial( a, b ) {
747
+ const loader = this.loader; // final results
573
748
 
574
- if ( a.colourCode === b.colourCode ) {
749
+ const faces = [];
750
+ const lineSegments = [];
751
+ const conditionalSegments = [];
752
+ const subobjects = [];
753
+ const materials = {};
575
754
 
576
- return 0;
755
+ const getLocalMaterial = colorCode => {
577
756
 
578
- }
757
+ return materials[ colorCode ] || null;
579
758
 
580
- if ( a.colourCode < b.colourCode ) {
759
+ };
581
760
 
582
- return - 1;
761
+ let type = 'Model';
762
+ let category = null;
763
+ let keywords = null;
764
+ let totalFaces = 0; // split into lines
583
765
 
584
- }
766
+ if ( text.indexOf( '\r\n' ) !== - 1 ) {
585
767
 
586
- return 1;
768
+ // This is faster than String.split with regex that splits on both
769
+ text = text.replace( /\r\n/g, '\n' );
587
770
 
588
- }
771
+ }
589
772
 
590
- function createObject( elements, elementSize, isConditionalSegments = false, totalElements = null ) {
773
+ const lines = text.split( '\n' );
774
+ const numLines = lines.length;
775
+ let parsingEmbeddedFiles = false;
776
+ let currentEmbeddedFileName = null;
777
+ let currentEmbeddedText = null;
778
+ let bfcCertified = false;
779
+ let bfcCCW = true;
780
+ let bfcInverted = false;
781
+ let bfcCull = true;
782
+ let startingConstructionStep = false; // Parse all line commands
591
783
 
592
- // Creates a THREE.LineSegments (elementSize = 2) or a THREE.Mesh (elementSize = 3 )
593
- // With per face / segment material, implemented with mesh groups and materials array
594
- // Sort the faces or line segments by colour code to make later the mesh groups
595
- elements.sort( sortByMaterial );
784
+ for ( let lineIndex = 0; lineIndex < numLines; lineIndex ++ ) {
596
785
 
597
- if ( totalElements === null ) {
786
+ const line = lines[ lineIndex ];
787
+ if ( line.length === 0 ) continue;
598
788
 
599
- totalElements = elements.length;
789
+ if ( parsingEmbeddedFiles ) {
600
790
 
601
- }
791
+ if ( line.startsWith( '0 FILE ' ) ) {
602
792
 
603
- const positions = new Float32Array( elementSize * totalElements * 3 );
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;
793
+ // Save previous embedded file in the cache
794
+ this.setData( currentEmbeddedFileName, currentEmbeddedText ); // New embedded text file
612
795
 
613
- for ( let iElem = 0, nElem = elements.length; iElem < nElem; iElem ++ ) {
796
+ currentEmbeddedFileName = line.substring( 7 );
797
+ currentEmbeddedText = '';
614
798
 
615
- const elem = elements[ iElem ];
616
- let vertices = elem.vertices;
799
+ } else {
617
800
 
618
- if ( vertices.length === 4 ) {
801
+ currentEmbeddedText += line + '\n';
619
802
 
620
- quadArray[ 0 ] = vertices[ 0 ];
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;
803
+ }
627
804
 
628
- }
805
+ continue;
629
806
 
630
- for ( let j = 0, l = vertices.length; j < l; j ++ ) {
807
+ }
631
808
 
632
- const v = vertices[ j ];
633
- const index = offset + j * 3;
634
- positions[ index + 0 ] = v.x;
635
- positions[ index + 1 ] = v.y;
636
- positions[ index + 2 ] = v.z;
809
+ const lp = new LineParser( line, lineIndex + 1 );
810
+ lp.seekNonSpace();
637
811
 
638
- }
812
+ if ( lp.isAtTheEnd() ) {
639
813
 
640
- if ( elementSize === 3 ) {
814
+ // Empty line
815
+ continue;
641
816
 
642
- let elemNormals = elem.normals;
817
+ } // Parse the line type
643
818
 
644
- if ( elemNormals.length === 4 ) {
645
819
 
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;
820
+ const lineType = lp.getToken();
821
+ let material;
822
+ let colorCode;
823
+ let segment;
824
+ let ccw;
825
+ let doubleSided;
826
+ let v0, v1, v2, v3, c0, c1;
653
827
 
654
- }
828
+ switch ( lineType ) {
655
829
 
656
- for ( let j = 0, l = elemNormals.length; j < l; j ++ ) {
830
+ // Line type 0: Comment or META
831
+ case '0':
832
+ // Parse meta directive
833
+ const meta = lp.getToken();
657
834
 
658
- let n = elem.faceNormal;
835
+ if ( meta ) {
659
836
 
660
- if ( elemNormals[ j ] ) {
837
+ switch ( meta ) {
661
838
 
662
- n = elemNormals[ j ].norm;
839
+ case '!LDRAW_ORG':
840
+ type = lp.getToken();
841
+ break;
663
842
 
664
- }
843
+ case '!COLOUR':
844
+ material = loader.parseColorMetaDirective( lp );
665
845
 
666
- const index = offset + j * 3;
667
- normals[ index + 0 ] = n.x;
668
- normals[ index + 1 ] = n.y;
669
- normals[ index + 2 ] = n.z;
846
+ if ( material ) {
670
847
 
671
- }
848
+ materials[ material.userData.code ] = material;
672
849
 
673
- }
850
+ } else {
674
851
 
675
- if ( prevMaterial !== elem.material ) {
852
+ console.warn( 'LDrawLoader: Error parsing material' + lp.getLineNumberString() );
676
853
 
677
- if ( prevMaterial !== null ) {
854
+ }
678
855
 
679
- bufferGeometry.addGroup( index0, numGroupVerts, materials.length - 1 );
856
+ break;
680
857
 
681
- }
858
+ case '!CATEGORY':
859
+ category = lp.getToken();
860
+ break;
682
861
 
683
- materials.push( elem.material );
684
- prevMaterial = elem.material;
685
- index0 = offset / 3;
686
- numGroupVerts = vertices.length;
862
+ case '!KEYWORDS':
863
+ const newKeywords = lp.getRemainingString().split( ',' );
687
864
 
688
- } else {
865
+ if ( newKeywords.length > 0 ) {
689
866
 
690
- numGroupVerts += vertices.length;
867
+ if ( ! keywords ) {
691
868
 
692
- }
869
+ keywords = [];
693
870
 
694
- offset += 3 * vertices.length;
871
+ }
695
872
 
696
- }
873
+ newKeywords.forEach( function ( keyword ) {
697
874
 
698
- if ( numGroupVerts > 0 ) {
875
+ keywords.push( keyword.trim() );
699
876
 
700
- bufferGeometry.addGroup( index0, Infinity, materials.length - 1 );
701
-
702
- }
877
+ } );
703
878
 
704
- bufferGeometry.setAttribute( 'position', new THREE.BufferAttribute( positions, 3 ) );
879
+ }
705
880
 
706
- if ( normals !== null ) {
881
+ break;
707
882
 
708
- bufferGeometry.setAttribute( 'normal', new THREE.BufferAttribute( normals, 3 ) );
883
+ case 'FILE':
884
+ if ( lineIndex > 0 ) {
709
885
 
710
- }
886
+ // Start embedded text files parsing
887
+ parsingEmbeddedFiles = true;
888
+ currentEmbeddedFileName = lp.getRemainingString();
889
+ currentEmbeddedText = '';
890
+ bfcCertified = false;
891
+ bfcCCW = true;
711
892
 
712
- let object3d = null;
893
+ }
713
894
 
714
- if ( elementSize === 2 ) {
895
+ break;
715
896
 
716
- object3d = new THREE.LineSegments( bufferGeometry, materials.length === 1 ? materials[ 0 ] : materials );
897
+ case 'BFC':
898
+ // Changes to the backface culling state
899
+ while ( ! lp.isAtTheEnd() ) {
717
900
 
718
- } else if ( elementSize === 3 ) {
901
+ const token = lp.getToken();
719
902
 
720
- object3d = new THREE.Mesh( bufferGeometry, materials.length === 1 ? materials[ 0 ] : materials );
903
+ switch ( token ) {
721
904
 
722
- }
905
+ case 'CERTIFY':
906
+ case 'NOCERTIFY':
907
+ bfcCertified = token === 'CERTIFY';
908
+ bfcCCW = true;
909
+ break;
723
910
 
724
- if ( isConditionalSegments ) {
911
+ case 'CW':
912
+ case 'CCW':
913
+ bfcCCW = token === 'CCW';
914
+ break;
725
915
 
726
- object3d.isConditionalLine = true;
727
- const controlArray0 = new Float32Array( elements.length * 3 * 2 );
728
- const controlArray1 = new Float32Array( elements.length * 3 * 2 );
729
- const directionArray = new Float32Array( elements.length * 3 * 2 );
916
+ case 'INVERTNEXT':
917
+ bfcInverted = true;
918
+ break;
730
919
 
731
- for ( let i = 0, l = elements.length; i < l; i ++ ) {
920
+ case 'CLIP':
921
+ case 'NOCLIP':
922
+ bfcCull = token === 'CLIP';
923
+ break;
732
924
 
733
- const os = elements[ i ];
734
- const vertices = os.vertices;
735
- const controlPoints = os.controlPoints;
736
- const c0 = controlPoints[ 0 ];
737
- const c1 = controlPoints[ 1 ];
738
- const v0 = vertices[ 0 ];
739
- const v1 = vertices[ 1 ];
740
- const index = i * 3 * 2;
741
- controlArray0[ index + 0 ] = c0.x;
742
- controlArray0[ index + 1 ] = c0.y;
743
- controlArray0[ index + 2 ] = c0.z;
744
- controlArray0[ index + 3 ] = c0.x;
745
- controlArray0[ index + 4 ] = c0.y;
746
- controlArray0[ index + 5 ] = c0.z;
747
- controlArray1[ index + 0 ] = c1.x;
748
- controlArray1[ index + 1 ] = c1.y;
749
- controlArray1[ index + 2 ] = c1.z;
750
- controlArray1[ index + 3 ] = c1.x;
751
- controlArray1[ index + 4 ] = c1.y;
752
- controlArray1[ index + 5 ] = c1.z;
753
- directionArray[ index + 0 ] = v1.x - v0.x;
754
- directionArray[ index + 1 ] = v1.y - v0.y;
755
- directionArray[ index + 2 ] = v1.z - v0.z;
756
- directionArray[ index + 3 ] = v1.x - v0.x;
757
- directionArray[ index + 4 ] = v1.y - v0.y;
758
- directionArray[ index + 5 ] = v1.z - v0.z;
925
+ default:
926
+ console.warn( 'THREE.LDrawLoader: BFC directive "' + token + '" is unknown.' );
927
+ break;
759
928
 
760
- }
929
+ }
761
930
 
762
- bufferGeometry.setAttribute( 'control0', new THREE.BufferAttribute( controlArray0, 3, false ) );
763
- bufferGeometry.setAttribute( 'control1', new THREE.BufferAttribute( controlArray1, 3, false ) );
764
- bufferGeometry.setAttribute( 'direction', new THREE.BufferAttribute( directionArray, 3, false ) );
931
+ }
765
932
 
766
- }
933
+ break;
767
934
 
768
- return object3d;
935
+ case 'STEP':
936
+ startingConstructionStep = true;
937
+ break;
769
938
 
770
- } //
939
+ default:
940
+ // Other meta directives are not implemented
941
+ break;
771
942
 
943
+ }
772
944
 
773
- class LDrawLoader extends THREE.Loader {
945
+ }
774
946
 
775
- constructor( manager ) {
947
+ break;
948
+ // Line type 1: Sub-object file
776
949
 
777
- super( manager ); // Array of THREE.Material
950
+ case '1':
951
+ colorCode = lp.getToken();
952
+ material = getLocalMaterial( colorCode );
953
+ const posX = parseFloat( lp.getToken() );
954
+ const posY = parseFloat( lp.getToken() );
955
+ const posZ = parseFloat( lp.getToken() );
956
+ const m0 = parseFloat( lp.getToken() );
957
+ const m1 = parseFloat( lp.getToken() );
958
+ const m2 = parseFloat( lp.getToken() );
959
+ const m3 = parseFloat( lp.getToken() );
960
+ const m4 = parseFloat( lp.getToken() );
961
+ const m5 = parseFloat( lp.getToken() );
962
+ const m6 = parseFloat( lp.getToken() );
963
+ const m7 = parseFloat( lp.getToken() );
964
+ const m8 = parseFloat( lp.getToken() );
965
+ const matrix = new THREE.Matrix4().set( m0, m1, m2, posX, m3, m4, m5, posY, m6, m7, m8, posZ, 0, 0, 0, 1 );
966
+ let fileName = lp.getRemainingString().trim().replace( /\\/g, '/' );
778
967
 
779
- this.materials = []; // Not using THREE.Cache here because it returns the previous HTML error response instead of calling onError()
780
- // This also allows to handle the embedded text files ("0 FILE" lines)
968
+ if ( loader.fileMap[ fileName ] ) {
781
969
 
782
- this.cache = new LDrawFileCache( 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.
970
+ // Found the subobject path in the preloaded file path map
971
+ fileName = loader.fileMap[ fileName ];
783
972
 
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)
973
+ } else {
786
974
 
787
- this.setMaterials( [ this.parseColourMetaDirective( new LineParser( 'Main_Colour CODE 16 VALUE #FF8080 EDGE #333333' ) ), this.parseColourMetaDirective( new LineParser( 'Edge_Colour CODE 24 VALUE #A0A0A0 EDGE #333333' ) ) ] ); // If this flag is set to true, each subobject will be a Object.
788
- // If not (the default), only one object which contains all the merged primitives will be created.
975
+ // Standardized subfolders
976
+ if ( fileName.startsWith( 's/' ) ) {
789
977
 
790
- this.separateObjects = false; // If this flag is set to true the vertex normals will be smoothed.
978
+ fileName = 'parts/' + fileName;
791
979
 
792
- this.smoothNormals = true; // The path to load parts from the LDraw parts library from.
980
+ } else if ( fileName.startsWith( '48/' ) ) {
793
981
 
794
- this.partsLibraryPath = '';
982
+ fileName = 'p/' + fileName;
795
983
 
796
- }
984
+ }
797
985
 
798
- setPartsLibraryPath( path ) {
986
+ }
799
987
 
800
- this.partsLibraryPath = path;
801
- return this;
988
+ subobjects.push( {
989
+ material: material,
990
+ colorCode: colorCode,
991
+ matrix: matrix,
992
+ fileName: fileName,
993
+ inverted: bfcInverted,
994
+ startingConstructionStep: startingConstructionStep
995
+ } );
996
+ bfcInverted = false;
997
+ break;
998
+ // Line type 2: Line segment
802
999
 
803
- }
1000
+ case '2':
1001
+ colorCode = lp.getToken();
1002
+ material = getLocalMaterial( colorCode );
1003
+ v0 = lp.getVector();
1004
+ v1 = lp.getVector();
1005
+ segment = {
1006
+ material: material,
1007
+ colorCode: colorCode,
1008
+ vertices: [ v0, v1 ]
1009
+ };
1010
+ lineSegments.push( segment );
1011
+ break;
1012
+ // Line type 5: Conditional Line segment
804
1013
 
805
- async preloadMaterials( url ) {
1014
+ case '5':
1015
+ colorCode = lp.getToken();
1016
+ material = getLocalMaterial( colorCode );
1017
+ v0 = lp.getVector();
1018
+ v1 = lp.getVector();
1019
+ c0 = lp.getVector();
1020
+ c1 = lp.getVector();
1021
+ segment = {
1022
+ material: material,
1023
+ colorCode: colorCode,
1024
+ vertices: [ v0, v1 ],
1025
+ controlPoints: [ c0, c1 ]
1026
+ };
1027
+ conditionalSegments.push( segment );
1028
+ break;
1029
+ // Line type 3: Triangle
806
1030
 
807
- const fileLoader = new THREE.FileLoader( this.manager );
808
- fileLoader.setPath( this.path );
809
- fileLoader.setRequestHeader( this.requestHeader );
810
- fileLoader.setWithCredentials( this.withCredentials );
811
- const text = await fileLoader.loadAsync( url );
812
- const colorLineRegex = /^0 !COLOUR/;
813
- const lines = text.split( /[\n\r]/g );
814
- const materials = [];
1031
+ case '3':
1032
+ colorCode = lp.getToken();
1033
+ material = getLocalMaterial( colorCode );
1034
+ ccw = bfcCCW;
1035
+ doubleSided = ! bfcCertified || ! bfcCull;
815
1036
 
816
- for ( let i = 0, l = lines.length; i < l; i ++ ) {
1037
+ if ( ccw === true ) {
817
1038
 
818
- const line = lines[ i ];
1039
+ v0 = lp.getVector();
1040
+ v1 = lp.getVector();
1041
+ v2 = lp.getVector();
819
1042
 
820
- if ( colorLineRegex.test( line ) ) {
1043
+ } else {
821
1044
 
822
- const directive = line.replace( colorLineRegex, '' );
823
- const material = this.parseColourMetaDirective( new LineParser( directive ) );
824
- materials.push( material );
1045
+ v2 = lp.getVector();
1046
+ v1 = lp.getVector();
1047
+ v0 = lp.getVector();
825
1048
 
826
- }
1049
+ }
827
1050
 
828
- }
1051
+ faces.push( {
1052
+ material: material,
1053
+ colorCode: colorCode,
1054
+ faceNormal: null,
1055
+ vertices: [ v0, v1, v2 ],
1056
+ normals: [ null, null, null ]
1057
+ } );
1058
+ totalFaces ++;
829
1059
 
830
- this.setMaterials( materials );
1060
+ if ( doubleSided === true ) {
831
1061
 
832
- }
1062
+ faces.push( {
1063
+ material: material,
1064
+ colorCode: colorCode,
1065
+ faceNormal: null,
1066
+ vertices: [ v2, v1, v0 ],
1067
+ normals: [ null, null, null ]
1068
+ } );
1069
+ totalFaces ++;
833
1070
 
834
- load( url, onLoad, onProgress, onError ) {
1071
+ }
835
1072
 
836
- if ( ! this.fileMap ) {
1073
+ break;
1074
+ // Line type 4: Quadrilateral
837
1075
 
838
- this.fileMap = {};
1076
+ case '4':
1077
+ colorCode = lp.getToken();
1078
+ material = getLocalMaterial( colorCode );
1079
+ ccw = bfcCCW;
1080
+ doubleSided = ! bfcCertified || ! bfcCull;
839
1081
 
840
- }
1082
+ if ( ccw === true ) {
841
1083
 
842
- const fileLoader = new THREE.FileLoader( this.manager );
843
- fileLoader.setPath( this.path );
844
- fileLoader.setRequestHeader( this.requestHeader );
845
- fileLoader.setWithCredentials( this.withCredentials );
846
- fileLoader.load( url, text => {
1084
+ v0 = lp.getVector();
1085
+ v1 = lp.getVector();
1086
+ v2 = lp.getVector();
1087
+ v3 = lp.getVector();
847
1088
 
848
- this.processObject( text, null, url, this.rootParseScope ).then( function ( result ) {
1089
+ } else {
849
1090
 
850
- onLoad( result.groupObject );
1091
+ v3 = lp.getVector();
1092
+ v2 = lp.getVector();
1093
+ v1 = lp.getVector();
1094
+ v0 = lp.getVector();
851
1095
 
852
- } );
1096
+ } // specifically place the triangle diagonal in the v0 and v1 slots so we can
1097
+ // account for the doubling of vertices later when smoothing normals.
853
1098
 
854
- }, onProgress, onError );
855
1099
 
856
- }
1100
+ faces.push( {
1101
+ material: material,
1102
+ colorCode: colorCode,
1103
+ faceNormal: null,
1104
+ vertices: [ v0, v1, v2, v3 ],
1105
+ normals: [ null, null, null, null ]
1106
+ } );
1107
+ totalFaces += 2;
857
1108
 
858
- parse( text, path, onLoad ) {
1109
+ if ( doubleSided === true ) {
859
1110
 
860
- // Async parse. This function calls onParse with the parsed THREE.Object3D as parameter
861
- this.processObject( text, null, path, this.rootParseScope ).then( function ( result ) {
1111
+ faces.push( {
1112
+ material: material,
1113
+ colorCode: colorCode,
1114
+ faceNormal: null,
1115
+ vertices: [ v3, v2, v1, v0 ],
1116
+ normals: [ null, null, null, null ]
1117
+ } );
1118
+ totalFaces += 2;
862
1119
 
863
- onLoad( result.groupObject );
1120
+ }
864
1121
 
865
- } );
1122
+ break;
866
1123
 
867
- }
1124
+ default:
1125
+ throw new Error( 'LDrawLoader: Unknown line type "' + lineType + '"' + lp.getLineNumberString() + '.' );
868
1126
 
869
- setMaterials( materials ) {
1127
+ }
870
1128
 
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;
1129
+ }
876
1130
 
877
- }
1131
+ if ( parsingEmbeddedFiles ) {
878
1132
 
879
- setFileMap( fileMap ) {
1133
+ this.setData( currentEmbeddedFileName, currentEmbeddedText );
880
1134
 
881
- this.fileMap = fileMap;
882
- return this;
1135
+ }
883
1136
 
884
- }
1137
+ return {
1138
+ faces,
1139
+ conditionalSegments,
1140
+ lineSegments,
1141
+ type,
1142
+ category,
1143
+ keywords,
1144
+ subobjects,
1145
+ totalFaces,
1146
+ startingConstructionStep,
1147
+ materials,
1148
+ fileName,
1149
+ group: null
1150
+ };
885
1151
 
886
- newParseScopeLevel( materials = null, parentScope = null ) {
1152
+ } // returns an (optionally cloned) instance of the data
887
1153
 
888
- // Adds a new scope level, assign materials to it and returns it
889
- const matLib = {};
890
1154
 
891
- if ( materials ) {
1155
+ getData( fileName, clone = true ) {
892
1156
 
893
- for ( let i = 0, n = materials.length; i < n; i ++ ) {
1157
+ const key = fileName.toLowerCase();
1158
+ const result = this._cache[ key ];
894
1159
 
895
- const material = materials[ i ];
896
- matLib[ material.userData.code ] = material;
1160
+ if ( result === null || result instanceof Promise ) {
897
1161
 
898
- }
1162
+ return null;
899
1163
 
900
1164
  }
901
1165
 
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
- type: 'Model',
920
- groupObject: null,
921
- // If false, it is a root material scope previous to parse
922
- isFromParse: true,
923
- faces: [],
924
- lineSegments: [],
925
- conditionalSegments: [],
926
- totalFaces: 0,
927
- // If true, this object is the start of a construction step
928
- startingConstructionStep: false
929
- };
930
- return newParseScope;
1166
+ if ( clone ) {
931
1167
 
932
- }
1168
+ return this.cloneResult( result );
933
1169
 
934
- addMaterial( material, parseScope ) {
1170
+ } else {
935
1171
 
936
- // Adds a material to the material library which is on top of the parse scopes stack. And also to the materials array
937
- const matLib = parseScope.lib;
1172
+ return result;
938
1173
 
939
- if ( ! matLib[ material.userData.code ] ) {
1174
+ }
940
1175
 
941
- this.materials.push( material );
1176
+ } // kicks off a fetch and parse of the requested data if it hasn't already been loaded. Returns when
1177
+ // the data is ready to use and can be retrieved synchronously with "getData".
942
1178
 
943
- }
944
1179
 
945
- matLib[ material.userData.code ] = material;
946
- return this;
1180
+ async ensureDataLoaded( fileName ) {
947
1181
 
948
- }
1182
+ const key = fileName.toLowerCase();
1183
+
1184
+ if ( ! ( key in this._cache ) ) {
949
1185
 
950
- getMaterial( colourCode, parseScope = this.rootParseScope ) {
1186
+ // replace the promise with a copy of the parsed data for immediate processing
1187
+ this._cache[ key ] = this.fetchData( fileName ).then( text => {
951
1188
 
952
- // Given a colour code search its material in the parse scopes stack
953
- if ( colourCode.startsWith( '0x2' ) ) {
1189
+ const info = this.parse( text, fileName );
1190
+ this._cache[ key ] = info;
1191
+ return info;
954
1192
 
955
- // Special 'direct' material value (RGB colour)
956
- const colour = colourCode.substring( 3 );
957
- return this.parseColourMetaDirective( new LineParser( 'Direct_Color_' + colour + ' CODE -1 VALUE #' + colour + ' EDGE #' + colour + '' ) );
1193
+ } );
958
1194
 
959
1195
  }
960
1196
 
961
- while ( parseScope ) {
1197
+ await this._cache[ key ];
962
1198
 
963
- const material = parseScope.lib[ colourCode ];
1199
+ } // sets the data in the cache from parsed data
964
1200
 
965
- if ( material ) {
966
1201
 
967
- return material;
1202
+ setData( fileName, text ) {
968
1203
 
969
- } else {
970
-
971
- parseScope = parseScope.parentScope;
1204
+ const key = fileName.toLowerCase();
1205
+ this._cache[ key ] = this.parse( text, fileName );
972
1206
 
973
- }
1207
+ }
974
1208
 
975
- } // Material was not found
1209
+ } // returns the material for an associated color code. If the color code is 16 for a face or 24 for
1210
+ // an edge then the passthroughColorCode is used.
976
1211
 
977
1212
 
978
- return null;
1213
+ function getMaterialFromCode( colorCode, parentColorCode, materialHierarchy, forEdge ) {
979
1214
 
980
- }
1215
+ const isPassthrough = ! forEdge && colorCode === MAIN_COLOUR_CODE || forEdge && colorCode === MAIN_EDGE_COLOUR_CODE;
981
1216
 
982
- parseColourMetaDirective( lineParser ) {
1217
+ if ( isPassthrough ) {
983
1218
 
984
- // Parses a colour definition and returns a THREE.Material
985
- let code = null; // Triangle and line colours
1219
+ colorCode = parentColorCode;
986
1220
 
987
- let colour = 0xFF00FF;
988
- let edgeColour = 0xFF00FF; // Transparency
1221
+ }
989
1222
 
990
- let alpha = 1;
991
- let isTransparent = false; // Self-illumination:
1223
+ return materialHierarchy[ colorCode ] || null;
992
1224
 
993
- let luminance = 0;
994
- let finishType = FINISH_TYPE_DEFAULT;
995
- let edgeMaterial = null;
996
- const name = lineParser.getToken();
1225
+ } // Class used to parse and build LDraw parts as three.js objects and cache them if they're a "Part" type.
997
1226
 
998
- if ( ! name ) {
999
1227
 
1000
- throw 'LDrawLoader: Material name was expected after "!COLOUR tag' + lineParser.getLineNumberString() + '.';
1228
+ class LDrawPartsGeometryCache {
1001
1229
 
1002
- } // Parse tag tokens and their parameters
1230
+ constructor( loader ) {
1003
1231
 
1232
+ this.loader = loader;
1233
+ this.parseCache = new LDrawParsedCache( loader );
1234
+ this._cache = {};
1004
1235
 
1005
- let token = null;
1236
+ } // Convert the given file information into a mesh by processing subobjects.
1006
1237
 
1007
- while ( true ) {
1008
1238
 
1009
- token = lineParser.getToken();
1239
+ async processIntoMesh( info ) {
1010
1240
 
1011
- if ( ! token ) {
1241
+ const loader = this.loader;
1242
+ const parseCache = this.parseCache;
1243
+ const faceMaterials = new Set(); // Processes the part subobject information to load child parts and merge geometry onto part
1244
+ // piece object.
1012
1245
 
1013
- break;
1246
+ const processInfoSubobjects = async ( info, subobject = null ) => {
1014
1247
 
1015
- }
1248
+ const subobjects = info.subobjects;
1249
+ const promises = []; // Trigger load of all subobjects. If a subobject isn't a primitive then load it as a separate
1250
+ // group which lets instruction steps apply correctly.
1016
1251
 
1017
- switch ( token.toUpperCase() ) {
1252
+ for ( let i = 0, l = subobjects.length; i < l; i ++ ) {
1018
1253
 
1019
- case 'CODE':
1020
- code = lineParser.getToken();
1021
- break;
1254
+ const subobject = subobjects[ i ];
1255
+ const promise = parseCache.ensureDataLoaded( subobject.fileName ).then( () => {
1022
1256
 
1023
- case 'VALUE':
1024
- colour = lineParser.getToken();
1257
+ const subobjectInfo = parseCache.getData( subobject.fileName, false );
1025
1258
 
1026
- if ( colour.startsWith( '0x' ) ) {
1259
+ if ( ! isPrimitiveType( subobjectInfo.type ) ) {
1027
1260
 
1028
- colour = '#' + colour.substring( 2 );
1261
+ return this.loadModel( subobject.fileName ).catch( error => {
1029
1262
 
1030
- } else if ( ! colour.startsWith( '#' ) ) {
1263
+ console.warn( error );
1264
+ return null;
1031
1265
 
1032
- throw 'LDrawLoader: Invalid colour while parsing material' + lineParser.getLineNumberString() + '.';
1266
+ } );
1033
1267
 
1034
1268
  }
1035
1269
 
1036
- break;
1037
-
1038
- case 'EDGE':
1039
- edgeColour = lineParser.getToken();
1040
-
1041
- if ( edgeColour.startsWith( '0x' ) ) {
1270
+ return processInfoSubobjects( parseCache.getData( subobject.fileName ), subobject );
1042
1271
 
1043
- edgeColour = '#' + edgeColour.substring( 2 );
1272
+ } );
1273
+ promises.push( promise );
1044
1274
 
1045
- } else if ( ! edgeColour.startsWith( '#' ) ) {
1275
+ }
1046
1276
 
1047
- // Try to see if edge colour is a colour code
1048
- edgeMaterial = this.getMaterial( edgeColour );
1277
+ const group = new THREE.Group();
1278
+ group.userData.category = info.category;
1279
+ group.userData.keywords = info.keywords;
1280
+ info.group = group;
1281
+ const subobjectInfos = await Promise.all( promises );
1049
1282
 
1050
- if ( ! edgeMaterial ) {
1283
+ for ( let i = 0, l = subobjectInfos.length; i < l; i ++ ) {
1051
1284
 
1052
- throw 'LDrawLoader: Invalid edge colour while parsing material' + lineParser.getLineNumberString() + '.';
1285
+ const subobject = info.subobjects[ i ];
1286
+ const subobjectInfo = subobjectInfos[ i ];
1053
1287
 
1054
- } // Get the edge material for this triangle material
1288
+ if ( subobjectInfo === null ) {
1055
1289
 
1290
+ // the subobject failed to load
1291
+ continue;
1056
1292
 
1057
- edgeMaterial = edgeMaterial.userData.edgeMaterial;
1293
+ } // if the subobject was loaded as a separate group then apply the parent scopes materials
1058
1294
 
1059
- }
1060
1295
 
1061
- break;
1296
+ if ( subobjectInfo.isGroup ) {
1062
1297
 
1063
- case 'ALPHA':
1064
- alpha = parseInt( lineParser.getToken() );
1298
+ const subobjectGroup = subobjectInfo;
1299
+ subobject.matrix.decompose( subobjectGroup.position, subobjectGroup.quaternion, subobjectGroup.scale );
1300
+ subobjectGroup.userData.startingConstructionStep = subobject.startingConstructionStep;
1301
+ subobjectGroup.name = subobject.fileName;
1302
+ loader.applyMaterialsToMesh( subobjectGroup, subobject.colorCode, info.materials );
1303
+ group.add( subobjectGroup );
1304
+ continue;
1065
1305
 
1066
- if ( isNaN( alpha ) ) {
1306
+ } // add the subobject group if it has children in case it has both children and primitives
1067
1307
 
1068
- throw 'LDrawLoader: Invalid alpha value in material definition' + lineParser.getLineNumberString() + '.';
1069
1308
 
1070
- }
1309
+ if ( subobjectInfo.group.children.length ) {
1071
1310
 
1072
- alpha = Math.max( 0, Math.min( 1, alpha / 255 ) );
1311
+ group.add( subobjectInfo.group );
1073
1312
 
1074
- if ( alpha < 1 ) {
1313
+ } // transform the primitives into the local space of the parent piece and append them to
1314
+ // to the parent primitives list.
1075
1315
 
1076
- isTransparent = true;
1077
1316
 
1078
- }
1317
+ const parentLineSegments = info.lineSegments;
1318
+ const parentConditionalSegments = info.conditionalSegments;
1319
+ const parentFaces = info.faces;
1320
+ const lineSegments = subobjectInfo.lineSegments;
1321
+ const conditionalSegments = subobjectInfo.conditionalSegments;
1322
+ const faces = subobjectInfo.faces;
1323
+ const matrix = subobject.matrix;
1324
+ const inverted = subobject.inverted;
1325
+ const matrixScaleInverted = matrix.determinant() < 0;
1326
+ const colorCode = subobject.colorCode;
1327
+ const lineColorCode = colorCode === MAIN_COLOUR_CODE ? MAIN_EDGE_COLOUR_CODE : colorCode;
1079
1328
 
1080
- break;
1329
+ for ( let i = 0, l = lineSegments.length; i < l; i ++ ) {
1081
1330
 
1082
- case 'LUMINANCE':
1083
- luminance = parseInt( lineParser.getToken() );
1331
+ const ls = lineSegments[ i ];
1332
+ const vertices = ls.vertices;
1333
+ vertices[ 0 ].applyMatrix4( matrix );
1334
+ vertices[ 1 ].applyMatrix4( matrix );
1335
+ ls.colorCode = ls.colorCode === MAIN_EDGE_COLOUR_CODE ? lineColorCode : ls.colorCode;
1336
+ ls.material = ls.material || getMaterialFromCode( ls.colorCode, ls.colorCode, info.materials, true );
1337
+ parentLineSegments.push( ls );
1084
1338
 
1085
- if ( isNaN( luminance ) ) {
1339
+ }
1086
1340
 
1087
- throw 'LDrawLoader: Invalid luminance value in material definition' + LineParser.getLineNumberString() + '.';
1341
+ for ( let i = 0, l = conditionalSegments.length; i < l; i ++ ) {
1088
1342
 
1089
- }
1343
+ const os = conditionalSegments[ i ];
1344
+ const vertices = os.vertices;
1345
+ const controlPoints = os.controlPoints;
1346
+ vertices[ 0 ].applyMatrix4( matrix );
1347
+ vertices[ 1 ].applyMatrix4( matrix );
1348
+ controlPoints[ 0 ].applyMatrix4( matrix );
1349
+ controlPoints[ 1 ].applyMatrix4( matrix );
1350
+ os.colorCode = os.colorCode === MAIN_EDGE_COLOUR_CODE ? lineColorCode : os.colorCode;
1351
+ os.material = os.material || getMaterialFromCode( os.colorCode, os.colorCode, info.materials, true );
1352
+ parentConditionalSegments.push( os );
1090
1353
 
1091
- luminance = Math.max( 0, Math.min( 1, luminance / 255 ) );
1092
- break;
1354
+ }
1093
1355
 
1094
- case 'CHROME':
1095
- finishType = FINISH_TYPE_CHROME;
1096
- break;
1356
+ for ( let i = 0, l = faces.length; i < l; i ++ ) {
1097
1357
 
1098
- case 'PEARLESCENT':
1099
- finishType = FINISH_TYPE_PEARLESCENT;
1100
- break;
1358
+ const tri = faces[ i ];
1359
+ const vertices = tri.vertices;
1101
1360
 
1102
- case 'RUBBER':
1103
- finishType = FINISH_TYPE_RUBBER;
1104
- break;
1361
+ for ( let i = 0, l = vertices.length; i < l; i ++ ) {
1105
1362
 
1106
- case 'MATTE_METALLIC':
1107
- finishType = FINISH_TYPE_MATTE_METALLIC;
1108
- break;
1363
+ vertices[ i ].applyMatrix4( matrix );
1109
1364
 
1110
- case 'METAL':
1111
- finishType = FINISH_TYPE_METAL;
1112
- break;
1365
+ }
1113
1366
 
1114
- case 'MATERIAL':
1115
- // Not implemented
1116
- lineParser.setToEnd();
1117
- break;
1367
+ tri.colorCode = tri.colorCode === MAIN_COLOUR_CODE ? colorCode : tri.colorCode;
1368
+ tri.material = tri.material || getMaterialFromCode( tri.colorCode, colorCode, info.materials, false );
1369
+ faceMaterials.add( tri.colorCode ); // If the scale of the object is negated then the triangle winding order
1370
+ // needs to be flipped.
1118
1371
 
1119
- default:
1120
- throw 'LDrawLoader: Unknown token "' + token + '" while parsing material' + lineParser.getLineNumberString() + '.';
1121
- break;
1372
+ if ( matrixScaleInverted !== inverted ) {
1122
1373
 
1123
- }
1374
+ vertices.reverse();
1124
1375
 
1125
- }
1376
+ }
1126
1377
 
1127
- let material = null;
1378
+ parentFaces.push( tri );
1128
1379
 
1129
- switch ( finishType ) {
1380
+ }
1130
1381
 
1131
- case FINISH_TYPE_DEFAULT:
1132
- material = new THREE.MeshStandardMaterial( {
1133
- color: colour,
1134
- roughness: 0.3,
1135
- metalness: 0
1136
- } );
1137
- break;
1382
+ info.totalFaces += subobjectInfo.totalFaces;
1138
1383
 
1139
- case FINISH_TYPE_PEARLESCENT:
1140
- // Try to imitate pearlescency by making the surface glossy
1141
- material = new THREE.MeshStandardMaterial( {
1142
- color: colour,
1143
- roughness: 0.3,
1144
- metalness: 0.25
1145
- } );
1146
- break;
1384
+ } // Apply the parent subobjects pass through material code to this object. This is done several times due
1385
+ // to material scoping.
1147
1386
 
1148
- case FINISH_TYPE_CHROME:
1149
- // Mirror finish surface
1150
- material = new THREE.MeshStandardMaterial( {
1151
- color: colour,
1152
- roughness: 0,
1153
- metalness: 1
1154
- } );
1155
- break;
1156
1387
 
1157
- case FINISH_TYPE_RUBBER:
1158
- // Rubber finish
1159
- material = new THREE.MeshStandardMaterial( {
1160
- color: colour,
1161
- roughness: 0.9,
1162
- metalness: 0
1163
- } );
1164
- break;
1388
+ if ( subobject ) {
1165
1389
 
1166
- case FINISH_TYPE_MATTE_METALLIC:
1167
- // Brushed metal finish
1168
- material = new THREE.MeshStandardMaterial( {
1169
- color: colour,
1170
- roughness: 0.8,
1171
- metalness: 0.4
1172
- } );
1173
- break;
1390
+ loader.applyMaterialsToMesh( group, subobject.colorCode, info.materials );
1174
1391
 
1175
- case FINISH_TYPE_METAL:
1176
- // Average metal finish
1177
- material = new THREE.MeshStandardMaterial( {
1178
- color: colour,
1179
- roughness: 0.2,
1180
- metalness: 0.85
1181
- } );
1182
- break;
1392
+ }
1183
1393
 
1184
- default:
1185
- // Should not happen
1186
- break;
1394
+ return info;
1187
1395
 
1188
- }
1396
+ }; // Track material use to see if we need to use the normal smooth slow path for hard edges.
1189
1397
 
1190
- material.transparent = isTransparent;
1191
- material.premultipliedAlpha = true;
1192
- material.opacity = alpha;
1193
- material.depthWrite = ! isTransparent;
1194
- material.polygonOffset = true;
1195
- material.polygonOffsetFactor = 1;
1196
1398
 
1197
- if ( luminance !== 0 ) {
1399
+ for ( let i = 0, l = info.faces; i < l; i ++ ) {
1198
1400
 
1199
- material.emissive.set( material.color ).multiplyScalar( luminance );
1401
+ faceMaterials.add( info.faces[ i ].colorCode );
1200
1402
 
1201
1403
  }
1202
1404
 
1203
- if ( ! edgeMaterial ) {
1204
-
1205
- // This is the material used for edges
1206
- edgeMaterial = new THREE.LineBasicMaterial( {
1207
- color: edgeColour,
1208
- transparent: isTransparent,
1209
- opacity: alpha,
1210
- depthWrite: ! isTransparent
1211
- } );
1212
- edgeMaterial.userData.code = code;
1213
- edgeMaterial.name = name + ' - Edge'; // This is the material used for conditional edges
1214
-
1215
- edgeMaterial.userData.conditionalEdgeMaterial = new LDrawConditionalLineMaterial( {
1216
- fog: true,
1217
- transparent: isTransparent,
1218
- depthWrite: ! isTransparent,
1219
- color: edgeColour,
1220
- opacity: alpha
1221
- } );
1405
+ await processInfoSubobjects( info );
1222
1406
 
1223
- }
1407
+ if ( loader.smoothNormals ) {
1224
1408
 
1225
- material.userData.code = code;
1226
- material.name = name;
1227
- material.userData.edgeMaterial = edgeMaterial;
1228
- return material;
1409
+ const checkSubSegments = faceMaterials.size > 1;
1410
+ generateFaceNormals( info.faces );
1411
+ smoothNormals( info.faces, info.lineSegments, checkSubSegments );
1229
1412
 
1230
- } //
1413
+ } // Add the primitive objects and metadata.
1231
1414
 
1232
1415
 
1233
- objectParse( text, parseScope ) {
1416
+ const group = info.group;
1234
1417
 
1235
- // Retrieve data from the parent parse scope
1236
- const currentParseScope = parseScope;
1237
- const parentParseScope = currentParseScope.parentScope; // Main colour codes passed to this subobject (or default codes 16 and 24 if it is the root object)
1418
+ if ( info.faces.length > 0 ) {
1238
1419
 
1239
- const mainColourCode = currentParseScope.mainColourCode;
1240
- const mainEdgeColourCode = currentParseScope.mainEdgeColourCode; // Parse result variables
1420
+ group.add( createObject( info.faces, 3, false, info.totalFaces ) );
1241
1421
 
1242
- let faces;
1243
- let lineSegments;
1244
- let conditionalSegments;
1245
- const subobjects = [];
1246
- let category = null;
1247
- let keywords = null;
1422
+ }
1248
1423
 
1249
- if ( text.indexOf( '\r\n' ) !== - 1 ) {
1424
+ if ( info.lineSegments.length > 0 ) {
1250
1425
 
1251
- // This is faster than String.split with regex that splits on both
1252
- text = text.replace( /\r\n/g, '\n' );
1426
+ group.add( createObject( info.lineSegments, 2 ) );
1253
1427
 
1254
1428
  }
1255
1429
 
1256
- const lines = text.split( '\n' );
1257
- const numLines = lines.length;
1258
- let parsingEmbeddedFiles = false;
1259
- let currentEmbeddedFileName = null;
1260
- let currentEmbeddedText = null;
1261
- let bfcCertified = false;
1262
- let bfcCCW = true;
1263
- let bfcInverted = false;
1264
- let bfcCull = true;
1265
- let type = '';
1266
- let startingConstructionStep = false;
1267
- const scope = this;
1268
-
1269
- function parseColourCode( lineParser, forEdge ) {
1430
+ if ( info.conditionalSegments.length > 0 ) {
1270
1431
 
1271
- // Parses next colour code and returns a THREE.Material
1272
- let colourCode = lineParser.getToken();
1432
+ group.add( createObject( info.conditionalSegments, 2, true ) );
1273
1433
 
1274
- if ( ! forEdge && colourCode === '16' ) {
1434
+ }
1275
1435
 
1276
- colourCode = mainColourCode;
1436
+ return group;
1277
1437
 
1278
- }
1438
+ }
1279
1439
 
1280
- if ( forEdge && colourCode === '24' ) {
1440
+ hasCachedModel( fileName ) {
1281
1441
 
1282
- colourCode = mainEdgeColourCode;
1442
+ return fileName !== null && fileName.toLowerCase() in this._cache;
1283
1443
 
1284
- }
1444
+ }
1285
1445
 
1286
- const material = scope.getMaterial( colourCode, currentParseScope );
1446
+ async getCachedModel( fileName ) {
1287
1447
 
1288
- if ( ! material ) {
1448
+ if ( fileName !== null && this.hasCachedModel( fileName ) ) {
1289
1449
 
1290
- throw 'LDrawLoader: Unknown colour code "' + colourCode + '" is used' + lineParser.getLineNumberString() + ' but it was not defined previously.';
1450
+ const key = fileName.toLowerCase();
1451
+ const group = await this._cache[ key ];
1452
+ return group.clone();
1291
1453
 
1292
- }
1454
+ } else {
1293
1455
 
1294
- return material;
1456
+ return null;
1295
1457
 
1296
1458
  }
1297
1459
 
1298
- function parseVector( lp ) {
1460
+ } // Loads and parses the model with the given file name. Returns a cached copy if available.
1299
1461
 
1300
- const v = new THREE.Vector3( parseFloat( lp.getToken() ), parseFloat( lp.getToken() ), parseFloat( lp.getToken() ) );
1301
1462
 
1302
- if ( ! scope.separateObjects ) {
1463
+ async loadModel( fileName ) {
1303
1464
 
1304
- v.applyMatrix4( currentParseScope.currentMatrix );
1465
+ const parseCache = this.parseCache;
1466
+ const key = fileName.toLowerCase();
1305
1467
 
1306
- }
1468
+ if ( this.hasCachedModel( fileName ) ) {
1307
1469
 
1308
- return v;
1470
+ // Return cached model if available.
1471
+ return this.getCachedModel( fileName );
1309
1472
 
1310
- } // Parse all line commands
1473
+ } else {
1311
1474
 
1475
+ // Otherwise parse a new model.
1476
+ // Ensure the file data is loaded and pre parsed.
1477
+ await parseCache.ensureDataLoaded( fileName );
1478
+ const info = parseCache.getData( fileName );
1479
+ const promise = this.processIntoMesh( info ); // Now that the file has loaded it's possible that another part parse has been waiting in parallel
1480
+ // so check the cache again to see if it's been added since the last async operation so we don't
1481
+ // do unnecessary work.
1312
1482
 
1313
- for ( let lineIndex = 0; lineIndex < numLines; lineIndex ++ ) {
1483
+ if ( this.hasCachedModel( fileName ) ) {
1314
1484
 
1315
- const line = lines[ lineIndex ];
1316
- if ( line.length === 0 ) continue;
1485
+ return this.getCachedModel( fileName );
1317
1486
 
1318
- if ( parsingEmbeddedFiles ) {
1487
+ } // Cache object if it's a part so it can be reused later.
1319
1488
 
1320
- if ( line.startsWith( '0 FILE ' ) ) {
1321
1489
 
1322
- // Save previous embedded file in the cache
1323
- this.cache.setData( currentEmbeddedFileName.toLowerCase(), currentEmbeddedText ); // New embedded text file
1490
+ if ( isPartType( info.type ) ) {
1324
1491
 
1325
- currentEmbeddedFileName = line.substring( 7 );
1326
- currentEmbeddedText = '';
1492
+ this._cache[ key ] = promise;
1327
1493
 
1328
- } else {
1494
+ } // return a copy
1329
1495
 
1330
- currentEmbeddedText += line + '\n';
1331
1496
 
1332
- }
1497
+ const group = await promise;
1498
+ return group.clone();
1333
1499
 
1334
- continue;
1500
+ }
1335
1501
 
1336
- }
1502
+ } // parses the given model text into a renderable object. Returns cached copy if available.
1337
1503
 
1338
- const lp = new LineParser( line, lineIndex + 1 );
1339
- lp.seekNonSpace();
1340
1504
 
1341
- if ( lp.isAtTheEnd() ) {
1505
+ async parseModel( text ) {
1342
1506
 
1343
- // Empty line
1344
- continue;
1507
+ const parseCache = this.parseCache;
1508
+ const info = parseCache.parse( text );
1345
1509
 
1346
- } // Parse the line type
1510
+ if ( isPartType( info.type ) && this.hasCachedModel( info.fileName ) ) {
1347
1511
 
1512
+ return this.getCachedModel( info.fileName );
1348
1513
 
1349
- const lineType = lp.getToken();
1350
- let material;
1351
- let segment;
1352
- let inverted;
1353
- let ccw;
1354
- let doubleSided;
1355
- let v0, v1, v2, v3, c0, c1, faceNormal;
1514
+ }
1356
1515
 
1357
- switch ( lineType ) {
1516
+ return this.processIntoMesh( info );
1358
1517
 
1359
- // Line type 0: Comment or META
1360
- case '0':
1361
- // Parse meta directive
1362
- const meta = lp.getToken();
1518
+ }
1363
1519
 
1364
- if ( meta ) {
1520
+ }
1365
1521
 
1366
- switch ( meta ) {
1522
+ function sortByMaterial( a, b ) {
1367
1523
 
1368
- case '!LDRAW_ORG':
1369
- type = lp.getToken();
1370
- currentParseScope.type = type; // If the scale of the object is negated then the triangle winding order
1371
- // needs to be flipped.
1524
+ if ( a.colorCode === b.colorCode ) {
1372
1525
 
1373
- if ( currentParseScope.matrix.determinant() < 0 && ( scope.separateObjects && isPrimitiveType( type ) || ! scope.separateObjects ) ) {
1526
+ return 0;
1374
1527
 
1375
- currentParseScope.inverted = ! currentParseScope.inverted;
1528
+ }
1376
1529
 
1377
- }
1530
+ if ( a.colorCode < b.colorCode ) {
1378
1531
 
1379
- faces = currentParseScope.faces;
1380
- lineSegments = currentParseScope.lineSegments;
1381
- conditionalSegments = currentParseScope.conditionalSegments;
1382
- break;
1532
+ return - 1;
1383
1533
 
1384
- case '!COLOUR':
1385
- material = this.parseColourMetaDirective( lp );
1534
+ }
1386
1535
 
1387
- if ( material ) {
1536
+ return 1;
1388
1537
 
1389
- this.addMaterial( material, parseScope );
1538
+ }
1390
1539
 
1391
- } else {
1540
+ function createObject( elements, elementSize, isConditionalSegments = false, totalElements = null ) {
1392
1541
 
1393
- console.warn( 'LDrawLoader: Error parsing material' + lp.getLineNumberString() );
1542
+ // Creates a THREE.LineSegments (elementSize = 2) or a THREE.Mesh (elementSize = 3 )
1543
+ // With per face / segment material, implemented with mesh groups and materials array
1544
+ // Sort the faces or line segments by color code to make later the mesh groups
1545
+ elements.sort( sortByMaterial );
1394
1546
 
1395
- }
1547
+ if ( totalElements === null ) {
1396
1548
 
1397
- break;
1549
+ totalElements = elements.length;
1398
1550
 
1399
- case '!CATEGORY':
1400
- category = lp.getToken();
1401
- break;
1551
+ }
1402
1552
 
1403
- case '!KEYWORDS':
1404
- const newKeywords = lp.getRemainingString().split( ',' );
1553
+ const positions = new Float32Array( elementSize * totalElements * 3 );
1554
+ const normals = elementSize === 3 ? new Float32Array( elementSize * totalElements * 3 ) : null;
1555
+ const materials = [];
1556
+ const quadArray = new Array( 6 );
1557
+ const bufferGeometry = new THREE.BufferGeometry();
1558
+ let prevMaterial = null;
1559
+ let index0 = 0;
1560
+ let numGroupVerts = 0;
1561
+ let offset = 0;
1405
1562
 
1406
- if ( newKeywords.length > 0 ) {
1563
+ for ( let iElem = 0, nElem = elements.length; iElem < nElem; iElem ++ ) {
1407
1564
 
1408
- if ( ! keywords ) {
1565
+ const elem = elements[ iElem ];
1566
+ let vertices = elem.vertices;
1409
1567
 
1410
- keywords = [];
1568
+ if ( vertices.length === 4 ) {
1411
1569
 
1412
- }
1570
+ quadArray[ 0 ] = vertices[ 0 ];
1571
+ quadArray[ 1 ] = vertices[ 1 ];
1572
+ quadArray[ 2 ] = vertices[ 2 ];
1573
+ quadArray[ 3 ] = vertices[ 0 ];
1574
+ quadArray[ 4 ] = vertices[ 2 ];
1575
+ quadArray[ 5 ] = vertices[ 3 ];
1576
+ vertices = quadArray;
1413
1577
 
1414
- newKeywords.forEach( function ( keyword ) {
1578
+ }
1415
1579
 
1416
- keywords.push( keyword.trim() );
1580
+ for ( let j = 0, l = vertices.length; j < l; j ++ ) {
1417
1581
 
1418
- } );
1582
+ const v = vertices[ j ];
1583
+ const index = offset + j * 3;
1584
+ positions[ index + 0 ] = v.x;
1585
+ positions[ index + 1 ] = v.y;
1586
+ positions[ index + 2 ] = v.z;
1419
1587
 
1420
- }
1588
+ } // create the normals array if this is a set of faces
1421
1589
 
1422
- break;
1423
1590
 
1424
- case 'FILE':
1425
- if ( lineIndex > 0 ) {
1591
+ if ( elementSize === 3 ) {
1426
1592
 
1427
- // Start embedded text files parsing
1428
- parsingEmbeddedFiles = true;
1429
- currentEmbeddedFileName = lp.getRemainingString();
1430
- currentEmbeddedText = '';
1431
- bfcCertified = false;
1432
- bfcCCW = true;
1593
+ if ( ! elem.faceNormal ) {
1433
1594
 
1434
- }
1595
+ const v0 = vertices[ 0 ];
1596
+ const v1 = vertices[ 1 ];
1597
+ const v2 = vertices[ 2 ];
1435
1598
 
1436
- break;
1599
+ _tempVec0.subVectors( v1, v0 );
1437
1600
 
1438
- case 'BFC':
1439
- // Changes to the backface culling state
1440
- while ( ! lp.isAtTheEnd() ) {
1601
+ _tempVec1.subVectors( v2, v1 );
1441
1602
 
1442
- const token = lp.getToken();
1603
+ elem.faceNormal = new THREE.Vector3().crossVectors( _tempVec0, _tempVec1 ).normalize();
1443
1604
 
1444
- switch ( token ) {
1605
+ }
1445
1606
 
1446
- case 'CERTIFY':
1447
- case 'NOCERTIFY':
1448
- bfcCertified = token === 'CERTIFY';
1449
- bfcCCW = true;
1450
- break;
1607
+ let elemNormals = elem.normals;
1451
1608
 
1452
- case 'CW':
1453
- case 'CCW':
1454
- bfcCCW = token === 'CCW';
1455
- break;
1609
+ if ( elemNormals.length === 4 ) {
1456
1610
 
1457
- case 'INVERTNEXT':
1458
- bfcInverted = true;
1459
- break;
1611
+ quadArray[ 0 ] = elemNormals[ 0 ];
1612
+ quadArray[ 1 ] = elemNormals[ 1 ];
1613
+ quadArray[ 2 ] = elemNormals[ 2 ];
1614
+ quadArray[ 3 ] = elemNormals[ 0 ];
1615
+ quadArray[ 4 ] = elemNormals[ 2 ];
1616
+ quadArray[ 5 ] = elemNormals[ 3 ];
1617
+ elemNormals = quadArray;
1460
1618
 
1461
- case 'CLIP':
1462
- case 'NOCLIP':
1463
- bfcCull = token === 'CLIP';
1464
- break;
1619
+ }
1465
1620
 
1466
- default:
1467
- console.warn( 'THREE.LDrawLoader: BFC directive "' + token + '" is unknown.' );
1468
- break;
1621
+ for ( let j = 0, l = elemNormals.length; j < l; j ++ ) {
1469
1622
 
1470
- }
1623
+ // use face normal if a vertex normal is not provided
1624
+ let n = elem.faceNormal;
1471
1625
 
1472
- }
1626
+ if ( elemNormals[ j ] ) {
1473
1627
 
1474
- break;
1628
+ n = elemNormals[ j ].norm;
1475
1629
 
1476
- case 'STEP':
1477
- startingConstructionStep = true;
1478
- break;
1630
+ }
1479
1631
 
1480
- default:
1481
- // Other meta directives are not implemented
1482
- break;
1632
+ const index = offset + j * 3;
1633
+ normals[ index + 0 ] = n.x;
1634
+ normals[ index + 1 ] = n.y;
1635
+ normals[ index + 2 ] = n.z;
1483
1636
 
1484
- }
1637
+ }
1485
1638
 
1486
- }
1639
+ }
1487
1640
 
1488
- break;
1489
- // Line type 1: Sub-object file
1641
+ if ( prevMaterial !== elem.colorCode ) {
1490
1642
 
1491
- case '1':
1492
- material = parseColourCode( lp );
1493
- const posX = parseFloat( lp.getToken() );
1494
- const posY = parseFloat( lp.getToken() );
1495
- const posZ = parseFloat( lp.getToken() );
1496
- const m0 = parseFloat( lp.getToken() );
1497
- const m1 = parseFloat( lp.getToken() );
1498
- const m2 = parseFloat( lp.getToken() );
1499
- const m3 = parseFloat( lp.getToken() );
1500
- const m4 = parseFloat( lp.getToken() );
1501
- const m5 = parseFloat( lp.getToken() );
1502
- const m6 = parseFloat( lp.getToken() );
1503
- const m7 = parseFloat( lp.getToken() );
1504
- const m8 = parseFloat( lp.getToken() );
1505
- const matrix = new THREE.Matrix4().set( m0, m1, m2, posX, m3, m4, m5, posY, m6, m7, m8, posZ, 0, 0, 0, 1 );
1506
- let fileName = lp.getRemainingString().trim().replace( /\\/g, '/' );
1643
+ if ( prevMaterial !== null ) {
1507
1644
 
1508
- if ( scope.fileMap[ fileName ] ) {
1645
+ bufferGeometry.addGroup( index0, numGroupVerts, materials.length - 1 );
1509
1646
 
1510
- // Found the subobject path in the preloaded file path map
1511
- fileName = scope.fileMap[ fileName ];
1647
+ }
1512
1648
 
1513
- } else {
1649
+ const material = elem.material;
1514
1650
 
1515
- // Standardized subfolders
1516
- if ( fileName.startsWith( 's/' ) ) {
1651
+ if ( material !== null ) {
1517
1652
 
1518
- fileName = 'parts/' + fileName;
1653
+ if ( elementSize === 3 ) {
1519
1654
 
1520
- } else if ( fileName.startsWith( '48/' ) ) {
1655
+ materials.push( material );
1521
1656
 
1522
- fileName = 'p/' + fileName;
1657
+ } else if ( elementSize === 2 ) {
1523
1658
 
1524
- }
1659
+ if ( isConditionalSegments ) {
1660
+
1661
+ materials.push( material.userData.edgeMaterial.userData.conditionalEdgeMaterial );
1662
+
1663
+ } else {
1664
+
1665
+ materials.push( material.userData.edgeMaterial );
1525
1666
 
1526
1667
  }
1527
1668
 
1528
- subobjects.push( {
1529
- material: material,
1530
- matrix: matrix,
1531
- fileName: fileName,
1532
- inverted: bfcInverted !== currentParseScope.inverted,
1533
- startingConstructionStep: startingConstructionStep
1534
- } );
1535
- bfcInverted = false;
1536
- break;
1537
- // Line type 2: Line segment
1669
+ }
1538
1670
 
1539
- case '2':
1540
- material = parseColourCode( lp, true );
1541
- v0 = parseVector( lp );
1542
- v1 = parseVector( lp );
1543
- segment = {
1544
- material: material.userData.edgeMaterial,
1545
- colourCode: material.userData.code,
1546
- v0: v0,
1547
- v1: v1,
1548
- vertices: [ v0, v1 ]
1549
- };
1550
- lineSegments.push( segment );
1551
- break;
1552
- // Line type 5: Conditional Line segment
1671
+ } else {
1553
1672
 
1554
- case '5':
1555
- material = parseColourCode( lp, true );
1556
- v0 = parseVector( lp );
1557
- v1 = parseVector( lp );
1558
- c0 = parseVector( lp );
1559
- c1 = parseVector( lp );
1560
- segment = {
1561
- material: material.userData.edgeMaterial.userData.conditionalEdgeMaterial,
1562
- colourCode: material.userData.code,
1563
- vertices: [ v0, v1 ],
1564
- controlPoints: [ c0, c1 ]
1565
- };
1566
- conditionalSegments.push( segment );
1567
- break;
1568
- // Line type 3: Triangle
1673
+ // If a material has not been made available yet then keep the color code string in the material array
1674
+ // to save the spot for the material once a parent scopes materials are being applied to the object.
1675
+ materials.push( elem.colorCode );
1676
+
1677
+ }
1678
+
1679
+ prevMaterial = elem.colorCode;
1680
+ index0 = offset / 3;
1681
+ numGroupVerts = vertices.length;
1682
+
1683
+ } else {
1684
+
1685
+ numGroupVerts += vertices.length;
1686
+
1687
+ }
1688
+
1689
+ offset += 3 * vertices.length;
1690
+
1691
+ }
1692
+
1693
+ if ( numGroupVerts > 0 ) {
1694
+
1695
+ bufferGeometry.addGroup( index0, Infinity, materials.length - 1 );
1696
+
1697
+ }
1698
+
1699
+ bufferGeometry.setAttribute( 'position', new THREE.BufferAttribute( positions, 3 ) );
1700
+
1701
+ if ( normals !== null ) {
1702
+
1703
+ bufferGeometry.setAttribute( 'normal', new THREE.BufferAttribute( normals, 3 ) );
1704
+
1705
+ }
1706
+
1707
+ let object3d = null;
1708
+
1709
+ if ( elementSize === 2 ) {
1710
+
1711
+ if ( isConditionalSegments ) {
1712
+
1713
+ object3d = new ConditionalLineSegments( bufferGeometry, materials.length === 1 ? materials[ 0 ] : materials );
1714
+
1715
+ } else {
1716
+
1717
+ object3d = new THREE.LineSegments( bufferGeometry, materials.length === 1 ? materials[ 0 ] : materials );
1718
+
1719
+ }
1720
+
1721
+ } else if ( elementSize === 3 ) {
1722
+
1723
+ object3d = new THREE.Mesh( bufferGeometry, materials.length === 1 ? materials[ 0 ] : materials );
1724
+
1725
+ }
1726
+
1727
+ if ( isConditionalSegments ) {
1728
+
1729
+ object3d.isConditionalLine = true;
1730
+ const controlArray0 = new Float32Array( elements.length * 3 * 2 );
1731
+ const controlArray1 = new Float32Array( elements.length * 3 * 2 );
1732
+ const directionArray = new Float32Array( elements.length * 3 * 2 );
1733
+
1734
+ for ( let i = 0, l = elements.length; i < l; i ++ ) {
1735
+
1736
+ const os = elements[ i ];
1737
+ const vertices = os.vertices;
1738
+ const controlPoints = os.controlPoints;
1739
+ const c0 = controlPoints[ 0 ];
1740
+ const c1 = controlPoints[ 1 ];
1741
+ const v0 = vertices[ 0 ];
1742
+ const v1 = vertices[ 1 ];
1743
+ const index = i * 3 * 2;
1744
+ controlArray0[ index + 0 ] = c0.x;
1745
+ controlArray0[ index + 1 ] = c0.y;
1746
+ controlArray0[ index + 2 ] = c0.z;
1747
+ controlArray0[ index + 3 ] = c0.x;
1748
+ controlArray0[ index + 4 ] = c0.y;
1749
+ controlArray0[ index + 5 ] = c0.z;
1750
+ controlArray1[ index + 0 ] = c1.x;
1751
+ controlArray1[ index + 1 ] = c1.y;
1752
+ controlArray1[ index + 2 ] = c1.z;
1753
+ controlArray1[ index + 3 ] = c1.x;
1754
+ controlArray1[ index + 4 ] = c1.y;
1755
+ controlArray1[ index + 5 ] = c1.z;
1756
+ directionArray[ index + 0 ] = v1.x - v0.x;
1757
+ directionArray[ index + 1 ] = v1.y - v0.y;
1758
+ directionArray[ index + 2 ] = v1.z - v0.z;
1759
+ directionArray[ index + 3 ] = v1.x - v0.x;
1760
+ directionArray[ index + 4 ] = v1.y - v0.y;
1761
+ directionArray[ index + 5 ] = v1.z - v0.z;
1762
+
1763
+ }
1764
+
1765
+ bufferGeometry.setAttribute( 'control0', new THREE.BufferAttribute( controlArray0, 3, false ) );
1766
+ bufferGeometry.setAttribute( 'control1', new THREE.BufferAttribute( controlArray1, 3, false ) );
1767
+ bufferGeometry.setAttribute( 'direction', new THREE.BufferAttribute( directionArray, 3, false ) );
1768
+
1769
+ }
1770
+
1771
+ return object3d;
1772
+
1773
+ } //
1774
+
1775
+
1776
+ class LDrawLoader extends THREE.Loader {
1777
+
1778
+ constructor( manager ) {
1779
+
1780
+ super( manager ); // Array of THREE.Material
1781
+
1782
+ this.materials = [];
1783
+ this.materialLibrary = {}; // This also allows to handle the embedded text files ("0 FILE" lines)
1784
+
1785
+ this.partsCache = new LDrawPartsGeometryCache( this ); // This object is a map from file names to paths. It agilizes the paths search. If it is not set then files will be searched by trial and error.
1786
+
1787
+ this.fileMap = {}; // Initializes the materials library with default materials
1788
+
1789
+ this.setMaterials( [] ); // If this flag is set to true the vertex normals will be smoothed.
1790
+
1791
+ this.smoothNormals = true; // The path to load parts from the LDraw parts library from.
1792
+
1793
+ this.partsLibraryPath = '';
1794
+
1795
+ }
1796
+
1797
+ setPartsLibraryPath( path ) {
1798
+
1799
+ this.partsLibraryPath = path;
1800
+ return this;
1801
+
1802
+ }
1803
+
1804
+ async preloadMaterials( url ) {
1805
+
1806
+ const fileLoader = new THREE.FileLoader( this.manager );
1807
+ fileLoader.setPath( this.path );
1808
+ fileLoader.setRequestHeader( this.requestHeader );
1809
+ fileLoader.setWithCredentials( this.withCredentials );
1810
+ const text = await fileLoader.loadAsync( url );
1811
+ const colorLineRegex = /^0 !COLOUR/;
1812
+ const lines = text.split( /[\n\r]/g );
1813
+ const materials = [];
1814
+
1815
+ for ( let i = 0, l = lines.length; i < l; i ++ ) {
1816
+
1817
+ const line = lines[ i ];
1818
+
1819
+ if ( colorLineRegex.test( line ) ) {
1820
+
1821
+ const directive = line.replace( colorLineRegex, '' );
1822
+ const material = this.parseColorMetaDirective( new LineParser( directive ) );
1823
+ materials.push( material );
1824
+
1825
+ }
1826
+
1827
+ }
1828
+
1829
+ this.setMaterials( materials );
1830
+
1831
+ }
1832
+
1833
+ load( url, onLoad, onProgress, onError ) {
1834
+
1835
+ const fileLoader = new THREE.FileLoader( this.manager );
1836
+ fileLoader.setPath( this.path );
1837
+ fileLoader.setRequestHeader( this.requestHeader );
1838
+ fileLoader.setWithCredentials( this.withCredentials );
1839
+ fileLoader.load( url, text => {
1840
+
1841
+ this.partsCache.parseModel( text, this.materialLibrary ).then( group => {
1842
+
1843
+ this.applyMaterialsToMesh( group, MAIN_COLOUR_CODE, this.materialLibrary, true );
1844
+ this.computeConstructionSteps( group );
1845
+ onLoad( group );
1846
+
1847
+ } ).catch( onError );
1848
+
1849
+ }, onProgress, onError );
1850
+
1851
+ }
1852
+
1853
+ parse( text, onLoad ) {
1854
+
1855
+ this.partsCache.parseModel( text, this.materialLibrary ).then( group => {
1856
+
1857
+ this.computeConstructionSteps( group );
1858
+ onLoad( group );
1859
+
1860
+ } );
1861
+
1862
+ }
1863
+
1864
+ setMaterials( materials ) {
1865
+
1866
+ this.materialLibrary = {};
1867
+ this.materials = [];
1868
+
1869
+ for ( let i = 0, l = materials.length; i < l; i ++ ) {
1870
+
1871
+ this.addMaterial( materials[ i ] );
1872
+
1873
+ } // Add default main triangle and line edge materials (used in pieces that can be colored with a main color)
1874
+
1875
+
1876
+ this.addMaterial( this.parseColorMetaDirective( new LineParser( 'Main_Colour CODE 16 VALUE #FF8080 EDGE #333333' ) ) );
1877
+ this.addMaterial( this.parseColorMetaDirective( new LineParser( 'Edge_Colour CODE 24 VALUE #A0A0A0 EDGE #333333' ) ) );
1878
+ return this;
1879
+
1880
+ }
1881
+
1882
+ setFileMap( fileMap ) {
1883
+
1884
+ this.fileMap = fileMap;
1885
+ return this;
1886
+
1887
+ }
1888
+
1889
+ addMaterial( material ) {
1890
+
1891
+ // Adds a material to the material library which is on top of the parse scopes stack. And also to the materials array
1892
+ const matLib = this.materialLibrary;
1893
+
1894
+ if ( ! matLib[ material.userData.code ] ) {
1569
1895
 
1570
- case '3':
1571
- material = parseColourCode( lp );
1572
- inverted = currentParseScope.inverted;
1573
- ccw = bfcCCW !== inverted;
1574
- doubleSided = ! bfcCertified || ! bfcCull;
1896
+ this.materials.push( material );
1897
+ matLib[ material.userData.code ] = material;
1575
1898
 
1576
- if ( ccw === true ) {
1899
+ }
1577
1900
 
1578
- v0 = parseVector( lp );
1579
- v1 = parseVector( lp );
1580
- v2 = parseVector( lp );
1901
+ return this;
1581
1902
 
1582
- } else {
1903
+ }
1583
1904
 
1584
- v2 = parseVector( lp );
1585
- v1 = parseVector( lp );
1586
- v0 = parseVector( lp );
1905
+ getMaterial( colorCode ) {
1587
1906
 
1588
- }
1907
+ if ( colorCode.startsWith( '0x2' ) ) {
1589
1908
 
1590
- _tempVec0.subVectors( v1, v0 );
1909
+ // Special 'direct' material value (RGB color)
1910
+ const color = colorCode.substring( 3 );
1911
+ return this.parseColorMetaDirective( new LineParser( 'Direct_Color_' + color + ' CODE -1 VALUE #' + color + ' EDGE #' + color + '' ) );
1591
1912
 
1592
- _tempVec1.subVectors( v2, v1 );
1913
+ }
1593
1914
 
1594
- faceNormal = new THREE.Vector3().crossVectors( _tempVec0, _tempVec1 ).normalize();
1595
- faces.push( {
1596
- material: material,
1597
- colourCode: material.userData.code,
1598
- faceNormal: faceNormal,
1599
- vertices: [ v0, v1, v2 ],
1600
- normals: [ null, null, null ]
1601
- } );
1602
- currentParseScope.totalFaces ++;
1915
+ return this.materialLibrary[ colorCode ] || null;
1603
1916
 
1604
- if ( doubleSided === true ) {
1917
+ } // Applies the appropriate materials to a prebuilt hierarchy of geometry. Assumes that color codes are present
1918
+ // in the material array if they need to be filled in.
1605
1919
 
1606
- faces.push( {
1607
- material: material,
1608
- colourCode: material.userData.code,
1609
- faceNormal: faceNormal,
1610
- vertices: [ v2, v1, v0 ],
1611
- normals: [ null, null, null ]
1612
- } );
1613
- currentParseScope.totalFaces ++;
1614
1920
 
1615
- }
1921
+ applyMaterialsToMesh( group, parentColorCode, materialHierarchy, finalMaterialPass = false ) {
1616
1922
 
1617
- break;
1618
- // Line type 4: Quadrilateral
1923
+ // find any missing materials as indicated by a color code string and replace it with a material from the current material lib
1924
+ const loader = this;
1925
+ const parentIsPassthrough = parentColorCode === MAIN_COLOUR_CODE;
1926
+ group.traverse( c => {
1619
1927
 
1620
- case '4':
1621
- material = parseColourCode( lp );
1622
- inverted = currentParseScope.inverted;
1623
- ccw = bfcCCW !== inverted;
1624
- doubleSided = ! bfcCertified || ! bfcCull;
1928
+ if ( c.isMesh || c.isLineSegments ) {
1625
1929
 
1626
- if ( ccw === true ) {
1930
+ if ( Array.isArray( c.material ) ) {
1627
1931
 
1628
- v0 = parseVector( lp );
1629
- v1 = parseVector( lp );
1630
- v2 = parseVector( lp );
1631
- v3 = parseVector( lp );
1932
+ for ( let i = 0, l = c.material.length; i < l; i ++ ) {
1632
1933
 
1633
- } else {
1934
+ if ( ! c.material[ i ].isMaterial ) {
1634
1935
 
1635
- v3 = parseVector( lp );
1636
- v2 = parseVector( lp );
1637
- v1 = parseVector( lp );
1638
- v0 = parseVector( lp );
1936
+ c.material[ i ] = getMaterial( c, c.material[ i ] );
1937
+
1938
+ }
1639
1939
 
1640
1940
  }
1641
1941
 
1642
- _tempVec0.subVectors( v1, v0 );
1942
+ } else if ( ! c.material.isMaterial ) {
1643
1943
 
1644
- _tempVec1.subVectors( v2, v1 );
1944
+ c.material = getMaterial( c, c.material );
1645
1945
 
1646
- faceNormal = new THREE.Vector3().crossVectors( _tempVec0, _tempVec1 ).normalize(); // specifically place the triangle diagonal in the v0 and v1 slots so we can
1647
- // account for the doubling of vertices later when smoothing normals.
1946
+ }
1648
1947
 
1649
- faces.push( {
1650
- material: material,
1651
- colourCode: material.userData.code,
1652
- faceNormal: faceNormal,
1653
- vertices: [ v0, v1, v2, v3 ],
1654
- normals: [ null, null, null, null ]
1655
- } );
1656
- currentParseScope.totalFaces += 2;
1948
+ }
1657
1949
 
1658
- if ( doubleSided === true ) {
1950
+ } ); // Returns the appropriate material for the object (line or face) given color code. If the code is "pass through"
1951
+ // (24 for lines, 16 for edges) then the pass through color code is used. If that is also pass through then it's
1952
+ // simply returned for the subsequent material application.
1659
1953
 
1660
- faces.push( {
1661
- material: material,
1662
- colourCode: material.userData.code,
1663
- faceNormal: faceNormal,
1664
- vertices: [ v3, v2, v1, v0 ],
1665
- normals: [ null, null, null, null ]
1666
- } );
1667
- currentParseScope.totalFaces += 2;
1954
+ function getMaterial( c, colorCode ) {
1668
1955
 
1669
- }
1956
+ // if our parent is a passthrough color code and we don't have the current material color available then
1957
+ // return early.
1958
+ if ( parentIsPassthrough && ! ( colorCode in materialHierarchy ) && ! finalMaterialPass ) {
1670
1959
 
1671
- break;
1960
+ return colorCode;
1672
1961
 
1673
- default:
1674
- throw 'LDrawLoader: Unknown line type "' + lineType + '"' + lp.getLineNumberString() + '.';
1675
- break;
1962
+ }
1963
+
1964
+ const forEdge = c.isLineSegments || c.isConditionalLine;
1965
+ const isPassthrough = ! forEdge && colorCode === MAIN_COLOUR_CODE || forEdge && colorCode === MAIN_EDGE_COLOUR_CODE;
1966
+
1967
+ if ( isPassthrough ) {
1968
+
1969
+ colorCode = parentColorCode;
1676
1970
 
1677
1971
  }
1678
1972
 
1679
- }
1973
+ let material = null;
1680
1974
 
1681
- if ( parsingEmbeddedFiles ) {
1975
+ if ( colorCode in materialHierarchy ) {
1682
1976
 
1683
- this.cache.setData( currentEmbeddedFileName.toLowerCase(), currentEmbeddedText );
1977
+ material = materialHierarchy[ colorCode ];
1684
1978
 
1685
- }
1979
+ } else if ( finalMaterialPass ) {
1686
1980
 
1687
- currentParseScope.category = category;
1688
- currentParseScope.keywords = keywords;
1689
- currentParseScope.subobjects = subobjects;
1690
- currentParseScope.numSubobjects = subobjects.length;
1691
- currentParseScope.subobjectIndex = 0;
1692
- const isRoot = ! parentParseScope.isFromParse;
1981
+ // see if we can get the final material from from the "getMaterial" function which will attempt to
1982
+ // parse the "direct" colors
1983
+ material = loader.getMaterial( colorCode );
1693
1984
 
1694
- if ( isRoot || scope.separateObjects && ! isPrimitiveType( type ) ) {
1985
+ if ( material === null ) {
1695
1986
 
1696
- currentParseScope.groupObject = new THREE.Group();
1697
- currentParseScope.groupObject.userData.startingConstructionStep = currentParseScope.startingConstructionStep;
1987
+ // otherwise throw an error if this is final opportunity to set the material
1988
+ throw new Error( `LDrawLoader: Material properties for code ${colorCode} not available.` );
1698
1989
 
1699
- }
1990
+ }
1700
1991
 
1701
- }
1992
+ } else {
1702
1993
 
1703
- computeConstructionSteps( model ) {
1994
+ return colorCode;
1704
1995
 
1705
- // Sets userdata.constructionStep number in THREE.Group objects and userData.numConstructionSteps number in the root THREE.Group object.
1706
- let stepNumber = 0;
1707
- model.traverse( c => {
1996
+ }
1708
1997
 
1709
- if ( c.isGroup ) {
1998
+ if ( c.isLineSegments ) {
1710
1999
 
1711
- if ( c.userData.startingConstructionStep ) {
2000
+ material = material.userData.edgeMaterial;
1712
2001
 
1713
- stepNumber ++;
2002
+ if ( c.isConditionalLine ) {
1714
2003
 
1715
- }
2004
+ material = material.userData.conditionalEdgeMaterial;
1716
2005
 
1717
- c.userData.constructionStep = stepNumber;
2006
+ }
1718
2007
 
1719
2008
  }
1720
2009
 
1721
- } );
1722
- model.userData.numConstructionSteps = stepNumber + 1;
2010
+ return material;
2011
+
2012
+ }
1723
2013
 
1724
2014
  }
1725
2015
 
1726
- finalizeObject( subobjectParseScope ) {
2016
+ getMainMaterial() {
1727
2017
 
1728
- // fail gracefully if an object could not be loaded
1729
- if ( subobjectParseScope === null ) {
2018
+ return this.getMaterial( MAIN_COLOUR_CODE );
1730
2019
 
1731
- return;
2020
+ }
1732
2021
 
1733
- }
2022
+ getMainEdgeMaterial() {
1734
2023
 
1735
- const parentParseScope = subobjectParseScope.parentScope; // Smooth the normals if this is a part or if this is a case where the subpart
1736
- // is added directly into the parent model (meaning it will never get smoothed by
1737
- // being added to a part)
2024
+ const mainMat = this.getMainMaterial();
2025
+ return mainMat && mainMat.userData ? mainMat.userData.edgeMaterial : null;
1738
2026
 
1739
- const doSmooth = isPartType( subobjectParseScope.type ) || ! isPartType( subobjectParseScope.type ) && ! isModelType( subobjectParseScope.type ) && isModelType( subobjectParseScope.parentScope.type );
2027
+ }
1740
2028
 
1741
- if ( this.smoothNormals && doSmooth ) {
2029
+ parseColorMetaDirective( lineParser ) {
1742
2030
 
1743
- smoothNormals( subobjectParseScope.faces, subobjectParseScope.lineSegments );
2031
+ // Parses a color definition and returns a THREE.Material
2032
+ let code = null; // Triangle and line colors
1744
2033
 
1745
- }
2034
+ let color = 0xFF00FF;
2035
+ let edgeColor = 0xFF00FF; // Transparency
1746
2036
 
1747
- const isRoot = ! parentParseScope.isFromParse;
2037
+ let alpha = 1;
2038
+ let isTransparent = false; // Self-illumination:
1748
2039
 
1749
- if ( this.separateObjects && ! isPrimitiveType( subobjectParseScope.type ) || isRoot ) {
2040
+ let luminance = 0;
2041
+ let finishType = FINISH_TYPE_DEFAULT;
2042
+ let edgeMaterial = null;
2043
+ const name = lineParser.getToken();
1750
2044
 
1751
- const objGroup = subobjectParseScope.groupObject;
2045
+ if ( ! name ) {
1752
2046
 
1753
- if ( subobjectParseScope.faces.length > 0 ) {
2047
+ throw new Error( 'LDrawLoader: Material name was expected after "!COLOUR tag' + lineParser.getLineNumberString() + '.' );
1754
2048
 
1755
- objGroup.add( createObject( subobjectParseScope.faces, 3, false, subobjectParseScope.totalFaces ) );
2049
+ } // Parse tag tokens and their parameters
1756
2050
 
1757
- }
1758
2051
 
1759
- if ( subobjectParseScope.lineSegments.length > 0 ) {
2052
+ let token = null;
1760
2053
 
1761
- objGroup.add( createObject( subobjectParseScope.lineSegments, 2 ) );
2054
+ while ( true ) {
1762
2055
 
1763
- }
2056
+ token = lineParser.getToken();
1764
2057
 
1765
- if ( subobjectParseScope.conditionalSegments.length > 0 ) {
2058
+ if ( ! token ) {
1766
2059
 
1767
- objGroup.add( createObject( subobjectParseScope.conditionalSegments, 2, true ) );
2060
+ break;
1768
2061
 
1769
2062
  }
1770
2063
 
1771
- if ( parentParseScope.groupObject ) {
1772
-
1773
- objGroup.name = subobjectParseScope.fileName;
1774
- objGroup.userData.category = subobjectParseScope.category;
1775
- objGroup.userData.keywords = subobjectParseScope.keywords;
1776
- subobjectParseScope.matrix.decompose( objGroup.position, objGroup.quaternion, objGroup.scale );
1777
- parentParseScope.groupObject.add( objGroup );
2064
+ switch ( token.toUpperCase() ) {
1778
2065
 
1779
- }
2066
+ case 'CODE':
2067
+ code = lineParser.getToken();
2068
+ break;
1780
2069
 
1781
- } else {
2070
+ case 'VALUE':
2071
+ color = lineParser.getToken();
1782
2072
 
1783
- const separateObjects = this.separateObjects;
1784
- const parentLineSegments = parentParseScope.lineSegments;
1785
- const parentConditionalSegments = parentParseScope.conditionalSegments;
1786
- const parentFaces = parentParseScope.faces;
1787
- const lineSegments = subobjectParseScope.lineSegments;
1788
- const conditionalSegments = subobjectParseScope.conditionalSegments;
1789
- const faces = subobjectParseScope.faces;
2073
+ if ( color.startsWith( '0x' ) ) {
1790
2074
 
1791
- for ( let i = 0, l = lineSegments.length; i < l; i ++ ) {
2075
+ color = '#' + color.substring( 2 );
1792
2076
 
1793
- const ls = lineSegments[ i ];
2077
+ } else if ( ! color.startsWith( '#' ) ) {
1794
2078
 
1795
- if ( separateObjects ) {
2079
+ throw new Error( 'LDrawLoader: Invalid color while parsing material' + lineParser.getLineNumberString() + '.' );
1796
2080
 
1797
- const vertices = ls.vertices;
1798
- vertices[ 0 ].applyMatrix4( subobjectParseScope.matrix );
1799
- vertices[ 1 ].applyMatrix4( subobjectParseScope.matrix );
2081
+ }
1800
2082
 
1801
- }
2083
+ break;
1802
2084
 
1803
- parentLineSegments.push( ls );
2085
+ case 'EDGE':
2086
+ edgeColor = lineParser.getToken();
1804
2087
 
1805
- }
2088
+ if ( edgeColor.startsWith( '0x' ) ) {
1806
2089
 
1807
- for ( let i = 0, l = conditionalSegments.length; i < l; i ++ ) {
2090
+ edgeColor = '#' + edgeColor.substring( 2 );
1808
2091
 
1809
- const os = conditionalSegments[ i ];
2092
+ } else if ( ! edgeColor.startsWith( '#' ) ) {
1810
2093
 
1811
- if ( separateObjects ) {
2094
+ // Try to see if edge color is a color code
2095
+ edgeMaterial = this.getMaterial( edgeColor );
1812
2096
 
1813
- const vertices = os.vertices;
1814
- const controlPoints = os.controlPoints;
1815
- vertices[ 0 ].applyMatrix4( subobjectParseScope.matrix );
1816
- vertices[ 1 ].applyMatrix4( subobjectParseScope.matrix );
1817
- controlPoints[ 0 ].applyMatrix4( subobjectParseScope.matrix );
1818
- controlPoints[ 1 ].applyMatrix4( subobjectParseScope.matrix );
2097
+ if ( ! edgeMaterial ) {
1819
2098
 
1820
- }
2099
+ throw new Error( 'LDrawLoader: Invalid edge color while parsing material' + lineParser.getLineNumberString() + '.' );
1821
2100
 
1822
- parentConditionalSegments.push( os );
2101
+ } // Get the edge material for this triangle material
1823
2102
 
1824
- }
1825
2103
 
1826
- for ( let i = 0, l = faces.length; i < l; i ++ ) {
2104
+ edgeMaterial = edgeMaterial.userData.edgeMaterial;
1827
2105
 
1828
- const tri = faces[ i ];
2106
+ }
1829
2107
 
1830
- if ( separateObjects ) {
2108
+ break;
1831
2109
 
1832
- const vertices = tri.vertices;
2110
+ case 'ALPHA':
2111
+ alpha = parseInt( lineParser.getToken() );
1833
2112
 
1834
- for ( let i = 0, l = vertices.length; i < l; i ++ ) {
2113
+ if ( isNaN( alpha ) ) {
1835
2114
 
1836
- vertices[ i ] = vertices[ i ].clone().applyMatrix4( subobjectParseScope.matrix );
2115
+ throw new Error( 'LDrawLoader: Invalid alpha value in material definition' + lineParser.getLineNumberString() + '.' );
1837
2116
 
1838
2117
  }
1839
2118
 
1840
- _tempVec0.subVectors( vertices[ 1 ], vertices[ 0 ] );
2119
+ alpha = Math.max( 0, Math.min( 1, alpha / 255 ) );
2120
+
2121
+ if ( alpha < 1 ) {
1841
2122
 
1842
- _tempVec1.subVectors( vertices[ 2 ], vertices[ 1 ] );
2123
+ isTransparent = true;
1843
2124
 
1844
- tri.faceNormal.crossVectors( _tempVec0, _tempVec1 ).normalize();
2125
+ }
1845
2126
 
1846
- }
2127
+ break;
1847
2128
 
1848
- parentFaces.push( tri );
2129
+ case 'LUMINANCE':
2130
+ luminance = parseInt( lineParser.getToken() );
1849
2131
 
1850
- }
2132
+ if ( isNaN( luminance ) ) {
1851
2133
 
1852
- parentParseScope.totalFaces += subobjectParseScope.totalFaces;
2134
+ throw new Error( 'LDrawLoader: Invalid luminance value in material definition' + LineParser.getLineNumberString() + '.' );
1853
2135
 
1854
- }
2136
+ }
1855
2137
 
1856
- }
2138
+ luminance = Math.max( 0, Math.min( 1, luminance / 255 ) );
2139
+ break;
1857
2140
 
1858
- async processObject( text, subobject, url, parentScope ) {
2141
+ case 'CHROME':
2142
+ finishType = FINISH_TYPE_CHROME;
2143
+ break;
1859
2144
 
1860
- const scope = this;
1861
- const parseScope = this.newParseScopeLevel( null, parentScope );
1862
- parseScope.url = url;
1863
- const parentParseScope = parseScope.parentScope; // Set current matrix
2145
+ case 'PEARLESCENT':
2146
+ finishType = FINISH_TYPE_PEARLESCENT;
2147
+ break;
1864
2148
 
1865
- if ( subobject ) {
2149
+ case 'RUBBER':
2150
+ finishType = FINISH_TYPE_RUBBER;
2151
+ break;
1866
2152
 
1867
- parseScope.currentMatrix.multiplyMatrices( parentParseScope.currentMatrix, subobject.matrix );
1868
- parseScope.matrix.copy( subobject.matrix );
1869
- parseScope.inverted = subobject.inverted;
1870
- parseScope.startingConstructionStep = subobject.startingConstructionStep;
1871
- parseScope.mainColourCode = subobject.material.userData.code;
1872
- parseScope.mainEdgeColourCode = subobject.material.userData.edgeMaterial.userData.code;
1873
- parseScope.fileName = subobject.fileName;
2153
+ case 'MATTE_METALLIC':
2154
+ finishType = FINISH_TYPE_MATTE_METALLIC;
2155
+ break;
1874
2156
 
1875
- } // Parse the object
2157
+ case 'METAL':
2158
+ finishType = FINISH_TYPE_METAL;
2159
+ break;
1876
2160
 
2161
+ case 'MATERIAL':
2162
+ // Not implemented
2163
+ lineParser.setToEnd();
2164
+ break;
1877
2165
 
1878
- this.objectParse( text, parseScope );
1879
- const subobjects = parseScope.subobjects;
1880
- const promises = [];
2166
+ default:
2167
+ throw new Error( 'LDrawLoader: Unknown token "' + token + '" while parsing material' + lineParser.getLineNumberString() + '.' );
1881
2168
 
1882
- for ( let i = 0, l = subobjects.length; i < l; i ++ ) {
2169
+ }
1883
2170
 
1884
- promises.push( loadSubobject( parseScope.subobjects[ i ] ) );
2171
+ }
1885
2172
 
1886
- } // Kick off of the downloads in parallel but process all the subobjects
1887
- // in order so all the assembly instructions are correct
2173
+ let material = null;
1888
2174
 
2175
+ switch ( finishType ) {
1889
2176
 
1890
- const subobjectScopes = await Promise.all( promises );
2177
+ case FINISH_TYPE_DEFAULT:
2178
+ material = new THREE.MeshStandardMaterial( {
2179
+ color: color,
2180
+ roughness: 0.3,
2181
+ metalness: 0
2182
+ } );
2183
+ break;
1891
2184
 
1892
- for ( let i = 0, l = subobjectScopes.length; i < l; i ++ ) {
2185
+ case FINISH_TYPE_PEARLESCENT:
2186
+ // Try to imitate pearlescency by making the surface glossy
2187
+ material = new THREE.MeshStandardMaterial( {
2188
+ color: color,
2189
+ roughness: 0.3,
2190
+ metalness: 0.25
2191
+ } );
2192
+ break;
1893
2193
 
1894
- this.finalizeObject( subobjectScopes[ i ] );
2194
+ case FINISH_TYPE_CHROME:
2195
+ // Mirror finish surface
2196
+ material = new THREE.MeshStandardMaterial( {
2197
+ color: color,
2198
+ roughness: 0,
2199
+ metalness: 1
2200
+ } );
2201
+ break;
1895
2202
 
1896
- } // If it is root object then finalize this object and compute construction steps
2203
+ case FINISH_TYPE_RUBBER:
2204
+ // Rubber finish
2205
+ material = new THREE.MeshStandardMaterial( {
2206
+ color: color,
2207
+ roughness: 0.9,
2208
+ metalness: 0
2209
+ } );
2210
+ break;
1897
2211
 
2212
+ case FINISH_TYPE_MATTE_METALLIC:
2213
+ // Brushed metal finish
2214
+ material = new THREE.MeshStandardMaterial( {
2215
+ color: color,
2216
+ roughness: 0.8,
2217
+ metalness: 0.4
2218
+ } );
2219
+ break;
1898
2220
 
1899
- if ( ! parentParseScope.isFromParse ) {
2221
+ case FINISH_TYPE_METAL:
2222
+ // Average metal finish
2223
+ material = new THREE.MeshStandardMaterial( {
2224
+ color: color,
2225
+ roughness: 0.2,
2226
+ metalness: 0.85
2227
+ } );
2228
+ break;
1900
2229
 
1901
- this.finalizeObject( parseScope );
1902
- this.computeConstructionSteps( parseScope.groupObject );
2230
+ default:
2231
+ // Should not happen
2232
+ break;
1903
2233
 
1904
2234
  }
1905
2235
 
1906
- return parseScope;
2236
+ material.transparent = isTransparent;
2237
+ material.premultipliedAlpha = true;
2238
+ material.opacity = alpha;
2239
+ material.depthWrite = ! isTransparent;
2240
+ material.color.convertSRGBToLinear();
2241
+ material.polygonOffset = true;
2242
+ material.polygonOffsetFactor = 1;
1907
2243
 
1908
- function loadSubobject( subobject ) {
2244
+ if ( luminance !== 0 ) {
1909
2245
 
1910
- return scope.cache.loadData( subobject.fileName ).then( function ( text ) {
2246
+ material.emissive.set( material.color ).multiplyScalar( luminance );
1911
2247
 
1912
- return scope.processObject( text, subobject, url, parseScope );
2248
+ }
1913
2249
 
1914
- } ).catch( function () {
2250
+ if ( ! edgeMaterial ) {
1915
2251
 
1916
- console.warn( 'LDrawLoader: Subobject "' + subobject.fileName + '" could not be found.' );
1917
- return null;
2252
+ // This is the material used for edges
2253
+ edgeMaterial = new THREE.LineBasicMaterial( {
2254
+ color: edgeColor,
2255
+ transparent: isTransparent,
2256
+ opacity: alpha,
2257
+ depthWrite: ! isTransparent
2258
+ } );
2259
+ edgeMaterial.userData.code = code;
2260
+ edgeMaterial.name = name + ' - Edge';
2261
+ edgeMaterial.color.convertSRGBToLinear(); // This is the material used for conditional edges
1918
2262
 
2263
+ edgeMaterial.userData.conditionalEdgeMaterial = new LDrawConditionalLineMaterial( {
2264
+ fog: true,
2265
+ transparent: isTransparent,
2266
+ depthWrite: ! isTransparent,
2267
+ color: edgeColor,
2268
+ opacity: alpha
1919
2269
  } );
2270
+ edgeMaterial.userData.conditionalEdgeMaterial.color.convertSRGBToLinear();
1920
2271
 
1921
2272
  }
1922
2273
 
2274
+ material.userData.code = code;
2275
+ material.name = name;
2276
+ material.userData.edgeMaterial = edgeMaterial;
2277
+ this.addMaterial( material );
2278
+ return material;
2279
+
2280
+ }
2281
+
2282
+ computeConstructionSteps( model ) {
2283
+
2284
+ // Sets userdata.constructionStep number in THREE.Group objects and userData.numConstructionSteps number in the root THREE.Group object.
2285
+ let stepNumber = 0;
2286
+ model.traverse( c => {
2287
+
2288
+ if ( c.isGroup ) {
2289
+
2290
+ if ( c.userData.startingConstructionStep ) {
2291
+
2292
+ stepNumber ++;
2293
+
2294
+ }
2295
+
2296
+ c.userData.constructionStep = stepNumber;
2297
+
2298
+ }
2299
+
2300
+ } );
2301
+ model.userData.numConstructionSteps = stepNumber + 1;
2302
+
1923
2303
  }
1924
2304
 
1925
2305
  }