sdl-mcp 0.12.2 → 0.12.3

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 (276) hide show
  1. package/README.md +10 -0
  2. package/config/sdlmcp.config.example.json +53 -8
  3. package/config/sdlmcp.config.schema.json +530 -106
  4. package/dist/.tsbuildinfo +1 -1
  5. package/dist/benchmark/external-manifest.d.ts +162 -0
  6. package/dist/benchmark/external-manifest.d.ts.map +1 -0
  7. package/dist/benchmark/external-manifest.js +336 -0
  8. package/dist/benchmark/external-manifest.js.map +1 -0
  9. package/dist/benchmark/external-runner.d.ts +100 -0
  10. package/dist/benchmark/external-runner.d.ts.map +1 -0
  11. package/dist/benchmark/external-runner.js +1171 -0
  12. package/dist/benchmark/external-runner.js.map +1 -0
  13. package/dist/benchmark/output-file.d.ts +3 -0
  14. package/dist/benchmark/output-file.d.ts.map +1 -0
  15. package/dist/benchmark/output-file.js +8 -0
  16. package/dist/benchmark/output-file.js.map +1 -0
  17. package/dist/cli/argParsing.d.ts.map +1 -1
  18. package/dist/cli/argParsing.js +6 -0
  19. package/dist/cli/argParsing.js.map +1 -1
  20. package/dist/cli/commands/benchmark.d.ts.map +1 -1
  21. package/dist/cli/commands/benchmark.js +3 -2
  22. package/dist/cli/commands/benchmark.js.map +1 -1
  23. package/dist/cli/commands/index.d.ts +1 -0
  24. package/dist/cli/commands/index.d.ts.map +1 -1
  25. package/dist/cli/commands/index.js +13 -7
  26. package/dist/cli/commands/index.js.map +1 -1
  27. package/dist/cli/commands/init.d.ts +5 -1
  28. package/dist/cli/commands/init.d.ts.map +1 -1
  29. package/dist/cli/commands/init.js +73 -3
  30. package/dist/cli/commands/init.js.map +1 -1
  31. package/dist/cli/commands/serve.d.ts.map +1 -1
  32. package/dist/cli/commands/serve.js +2 -0
  33. package/dist/cli/commands/serve.js.map +1 -1
  34. package/dist/cli/index.js +2 -0
  35. package/dist/cli/index.js.map +1 -1
  36. package/dist/cli/transport/http.d.ts +1 -35
  37. package/dist/cli/transport/http.d.ts.map +1 -1
  38. package/dist/cli/transport/http.js +151 -175
  39. package/dist/cli/transport/http.js.map +1 -1
  40. package/dist/cli/types.d.ts +1 -0
  41. package/dist/cli/types.d.ts.map +1 -1
  42. package/dist/config/constants.d.ts +30 -0
  43. package/dist/config/constants.d.ts.map +1 -1
  44. package/dist/config/constants.js +30 -0
  45. package/dist/config/constants.js.map +1 -1
  46. package/dist/config/types.d.ts +53 -1
  47. package/dist/config/types.d.ts.map +1 -1
  48. package/dist/config/types.js +62 -14
  49. package/dist/config/types.js.map +1 -1
  50. package/dist/db/ladybug-batching.d.ts +1 -0
  51. package/dist/db/ladybug-batching.d.ts.map +1 -1
  52. package/dist/db/ladybug-batching.js +1 -0
  53. package/dist/db/ladybug-batching.js.map +1 -1
  54. package/dist/db/ladybug-core.d.ts +3 -0
  55. package/dist/db/ladybug-core.d.ts.map +1 -1
  56. package/dist/db/ladybug-core.js +72 -2
  57. package/dist/db/ladybug-core.js.map +1 -1
  58. package/dist/db/ladybug-embeddings.d.ts +6 -1
  59. package/dist/db/ladybug-embeddings.d.ts.map +1 -1
  60. package/dist/db/ladybug-embeddings.js +6 -1
  61. package/dist/db/ladybug-embeddings.js.map +1 -1
  62. package/dist/db/ladybug-file-summaries.d.ts.map +1 -1
  63. package/dist/db/ladybug-file-summaries.js +23 -0
  64. package/dist/db/ladybug-file-summaries.js.map +1 -1
  65. package/dist/db/ladybug-queries.js +1 -1
  66. package/dist/db/ladybug-queries.js.map +1 -1
  67. package/dist/db/migrations/index.d.ts.map +1 -1
  68. package/dist/db/migrations/index.js +2 -0
  69. package/dist/db/migrations/index.js.map +1 -1
  70. package/dist/db/migrations/m007-copy-embeddings-to-symbol.d.ts +5 -9
  71. package/dist/db/migrations/m007-copy-embeddings-to-symbol.d.ts.map +1 -1
  72. package/dist/db/migrations/m007-copy-embeddings-to-symbol.js +24 -126
  73. package/dist/db/migrations/m007-copy-embeddings-to-symbol.js.map +1 -1
  74. package/dist/db/migrations/m021-remediate-symbol-embeddings.d.ts +5 -0
  75. package/dist/db/migrations/m021-remediate-symbol-embeddings.d.ts.map +1 -0
  76. package/dist/db/migrations/m021-remediate-symbol-embeddings.js +7 -0
  77. package/dist/db/migrations/m021-remediate-symbol-embeddings.js.map +1 -0
  78. package/dist/db/migrations/symbol-embedding-remediation.d.ts +57 -0
  79. package/dist/db/migrations/symbol-embedding-remediation.d.ts.map +1 -0
  80. package/dist/db/migrations/symbol-embedding-remediation.js +335 -0
  81. package/dist/db/migrations/symbol-embedding-remediation.js.map +1 -0
  82. package/dist/graph/layout/force-layout.d.ts +4 -0
  83. package/dist/graph/layout/force-layout.d.ts.map +1 -0
  84. package/dist/graph/layout/force-layout.js +127 -0
  85. package/dist/graph/layout/force-layout.js.map +1 -0
  86. package/dist/graph/layout/layout-service.d.ts +37 -0
  87. package/dist/graph/layout/layout-service.d.ts.map +1 -0
  88. package/dist/graph/layout/layout-service.js +172 -0
  89. package/dist/graph/layout/layout-service.js.map +1 -0
  90. package/dist/graph/layout/native-engine.d.ts +9 -0
  91. package/dist/graph/layout/native-engine.d.ts.map +1 -0
  92. package/dist/graph/layout/native-engine.js +59 -0
  93. package/dist/graph/layout/native-engine.js.map +1 -0
  94. package/dist/graph/layout/prng.d.ts +3 -0
  95. package/dist/graph/layout/prng.d.ts.map +1 -0
  96. package/dist/graph/layout/prng.js +19 -0
  97. package/dist/graph/layout/prng.js.map +1 -0
  98. package/dist/graph/layout/serializer.d.ts +3 -0
  99. package/dist/graph/layout/serializer.d.ts.map +1 -0
  100. package/dist/graph/layout/serializer.js +20 -0
  101. package/dist/graph/layout/serializer.js.map +1 -0
  102. package/dist/graph/layout/types.d.ts +30 -0
  103. package/dist/graph/layout/types.d.ts.map +1 -0
  104. package/dist/graph/layout/types.js +2 -0
  105. package/dist/graph/layout/types.js.map +1 -0
  106. package/dist/indexer/embeddings.d.ts +7 -0
  107. package/dist/indexer/embeddings.d.ts.map +1 -1
  108. package/dist/indexer/embeddings.js +44 -4
  109. package/dist/indexer/embeddings.js.map +1 -1
  110. package/dist/indexer/file-summary-embeddings.d.ts +12 -0
  111. package/dist/indexer/file-summary-embeddings.d.ts.map +1 -1
  112. package/dist/indexer/file-summary-embeddings.js +83 -21
  113. package/dist/indexer/file-summary-embeddings.js.map +1 -1
  114. package/dist/indexer/fileWalker.d.ts.map +1 -1
  115. package/dist/indexer/fileWalker.js +2 -1
  116. package/dist/indexer/fileWalker.js.map +1 -1
  117. package/dist/indexer/metrics-updater.d.ts.map +1 -1
  118. package/dist/indexer/metrics-updater.js +27 -4
  119. package/dist/indexer/metrics-updater.js.map +1 -1
  120. package/dist/indexer/watcher.d.ts.map +1 -1
  121. package/dist/indexer/watcher.js +2 -1
  122. package/dist/indexer/watcher.js.map +1 -1
  123. package/dist/mcp/context-response-projection.d.ts.map +1 -1
  124. package/dist/mcp/context-response-projection.js +49 -13
  125. package/dist/mcp/context-response-projection.js.map +1 -1
  126. package/dist/mcp/tools/search-edit/planner.d.ts.map +1 -1
  127. package/dist/mcp/tools/search-edit/planner.js +10 -3
  128. package/dist/mcp/tools/search-edit/planner.js.map +1 -1
  129. package/dist/observability/event-tap.d.ts +2 -1
  130. package/dist/observability/event-tap.d.ts.map +1 -1
  131. package/dist/observability/event-tap.js.map +1 -1
  132. package/dist/observability/service.d.ts +11 -4
  133. package/dist/observability/service.d.ts.map +1 -1
  134. package/dist/observability/service.js +83 -13
  135. package/dist/observability/service.js.map +1 -1
  136. package/dist/observability/types.d.ts +48 -2
  137. package/dist/observability/types.d.ts.map +1 -1
  138. package/dist/ui/.tsbuildinfo +1 -0
  139. package/dist/ui/vendor/fflate.js +2692 -0
  140. package/dist/ui/vendor/jsm/controls/ArcballControls.js +3543 -0
  141. package/dist/ui/vendor/jsm/controls/DragControls.js +452 -0
  142. package/dist/ui/vendor/jsm/controls/FirstPersonControls.js +488 -0
  143. package/dist/ui/vendor/jsm/controls/FlyControls.js +384 -0
  144. package/dist/ui/vendor/jsm/controls/MapControls.js +116 -0
  145. package/dist/ui/vendor/jsm/controls/OrbitControls.js +1963 -0
  146. package/dist/ui/vendor/jsm/controls/PointerLockControls.js +264 -0
  147. package/dist/ui/vendor/jsm/controls/TrackballControls.js +1001 -0
  148. package/dist/ui/vendor/jsm/controls/TransformControls.js +2003 -0
  149. package/dist/ui/vendor/jsm/loaders/3DMLoader.js +1836 -0
  150. package/dist/ui/vendor/jsm/loaders/3MFLoader.js +1621 -0
  151. package/dist/ui/vendor/jsm/loaders/AMFLoader.js +541 -0
  152. package/dist/ui/vendor/jsm/loaders/BVHLoader.js +484 -0
  153. package/dist/ui/vendor/jsm/loaders/ColladaLoader.js +153 -0
  154. package/dist/ui/vendor/jsm/loaders/DDSLoader.js +385 -0
  155. package/dist/ui/vendor/jsm/loaders/DRACOLoader.js +772 -0
  156. package/dist/ui/vendor/jsm/loaders/EXRLoader.js +3395 -0
  157. package/dist/ui/vendor/jsm/loaders/FBXLoader.js +4580 -0
  158. package/dist/ui/vendor/jsm/loaders/FontLoader.js +260 -0
  159. package/dist/ui/vendor/jsm/loaders/GCodeLoader.js +318 -0
  160. package/dist/ui/vendor/jsm/loaders/GLTFLoader.js +4860 -0
  161. package/dist/ui/vendor/jsm/loaders/HDRCubeTextureLoader.js +164 -0
  162. package/dist/ui/vendor/jsm/loaders/HDRLoader.js +463 -0
  163. package/dist/ui/vendor/jsm/loaders/IESLoader.js +379 -0
  164. package/dist/ui/vendor/jsm/loaders/KMZLoader.js +163 -0
  165. package/dist/ui/vendor/jsm/loaders/KTX2Loader.js +1276 -0
  166. package/dist/ui/vendor/jsm/loaders/KTXLoader.js +197 -0
  167. package/dist/ui/vendor/jsm/loaders/LDrawLoader.js +2510 -0
  168. package/dist/ui/vendor/jsm/loaders/LUT3dlLoader.js +205 -0
  169. package/dist/ui/vendor/jsm/loaders/LUTCubeLoader.js +190 -0
  170. package/dist/ui/vendor/jsm/loaders/LUTImageLoader.js +190 -0
  171. package/dist/ui/vendor/jsm/loaders/LWOLoader.js +1051 -0
  172. package/dist/ui/vendor/jsm/loaders/LottieLoader.js +130 -0
  173. package/dist/ui/vendor/jsm/loaders/MD2Loader.js +435 -0
  174. package/dist/ui/vendor/jsm/loaders/MDDLoader.js +147 -0
  175. package/dist/ui/vendor/jsm/loaders/MTLLoader.js +593 -0
  176. package/dist/ui/vendor/jsm/loaders/MaterialXLoader.js +1126 -0
  177. package/dist/ui/vendor/jsm/loaders/NRRDLoader.js +718 -0
  178. package/dist/ui/vendor/jsm/loaders/OBJLoader.js +955 -0
  179. package/dist/ui/vendor/jsm/loaders/PCDLoader.js +620 -0
  180. package/dist/ui/vendor/jsm/loaders/PDBLoader.js +272 -0
  181. package/dist/ui/vendor/jsm/loaders/PLYLoader.js +968 -0
  182. package/dist/ui/vendor/jsm/loaders/PVRLoader.js +270 -0
  183. package/dist/ui/vendor/jsm/loaders/RGBELoader.js +18 -0
  184. package/dist/ui/vendor/jsm/loaders/STLLoader.js +421 -0
  185. package/dist/ui/vendor/jsm/loaders/SVGLoader.js +3319 -0
  186. package/dist/ui/vendor/jsm/loaders/TDSLoader.js +1142 -0
  187. package/dist/ui/vendor/jsm/loaders/TGALoader.js +538 -0
  188. package/dist/ui/vendor/jsm/loaders/TIFFLoader.js +59 -0
  189. package/dist/ui/vendor/jsm/loaders/TTFLoader.js +261 -0
  190. package/dist/ui/vendor/jsm/loaders/USDLoader.js +303 -0
  191. package/dist/ui/vendor/jsm/loaders/USDZLoader.js +16 -0
  192. package/dist/ui/vendor/jsm/loaders/UltraHDRLoader.js +755 -0
  193. package/dist/ui/vendor/jsm/loaders/VOXLoader.js +919 -0
  194. package/dist/ui/vendor/jsm/loaders/VRMLLoader.js +3646 -0
  195. package/dist/ui/vendor/jsm/loaders/VTKLoader.js +1293 -0
  196. package/dist/ui/vendor/jsm/loaders/XYZLoader.js +143 -0
  197. package/dist/ui/vendor/jsm/loaders/collada/ColladaComposer.js +3044 -0
  198. package/dist/ui/vendor/jsm/loaders/collada/ColladaParser.js +1977 -0
  199. package/dist/ui/vendor/jsm/loaders/lwo/IFFParser.js +1217 -0
  200. package/dist/ui/vendor/jsm/loaders/lwo/LWO2Parser.js +414 -0
  201. package/dist/ui/vendor/jsm/loaders/lwo/LWO3Parser.js +373 -0
  202. package/dist/ui/vendor/jsm/loaders/usd/USDAParser.js +901 -0
  203. package/dist/ui/vendor/jsm/loaders/usd/USDCParser.js +1878 -0
  204. package/dist/ui/vendor/jsm/loaders/usd/USDComposer.js +4627 -0
  205. package/dist/ui/vendor/jsm/utils/BufferGeometryUtils.js +1501 -0
  206. package/dist/ui/vendor/jsm/utils/CameraUtils.js +82 -0
  207. package/dist/ui/vendor/jsm/utils/ColorUtils.js +76 -0
  208. package/dist/ui/vendor/jsm/utils/GeometryCompressionUtils.js +547 -0
  209. package/dist/ui/vendor/jsm/utils/GeometryUtils.js +226 -0
  210. package/dist/ui/vendor/jsm/utils/LDrawUtils.js +211 -0
  211. package/dist/ui/vendor/jsm/utils/SceneOptimizer.js +458 -0
  212. package/dist/ui/vendor/jsm/utils/SceneUtils.js +363 -0
  213. package/dist/ui/vendor/jsm/utils/ShadowMapViewer.js +244 -0
  214. package/dist/ui/vendor/jsm/utils/ShadowMapViewerGPU.js +233 -0
  215. package/dist/ui/vendor/jsm/utils/SkeletonUtils.js +496 -0
  216. package/dist/ui/vendor/jsm/utils/SortUtils.js +175 -0
  217. package/dist/ui/vendor/jsm/utils/UVsDebug.js +173 -0
  218. package/dist/ui/vendor/jsm/utils/WebGLTextureUtils.js +115 -0
  219. package/dist/ui/vendor/jsm/utils/WebGPUTextureUtils.js +81 -0
  220. package/dist/ui/vendor/jsm/utils/WorkerPool.js +167 -0
  221. package/dist/ui/vendor/three.core.min.js +6 -0
  222. package/dist/ui/vendor/three.module.min.js +6 -0
  223. package/dist/ui/viewer/ambient.js +39 -0
  224. package/dist/ui/viewer/api.js +93 -0
  225. package/dist/ui/viewer/chrome.js +36 -0
  226. package/dist/ui/viewer/edges.js +33 -0
  227. package/dist/ui/viewer/index.html +32 -0
  228. package/dist/ui/viewer/inspector.js +23 -0
  229. package/dist/ui/viewer/lenses/activity.js +24 -0
  230. package/dist/ui/viewer/lenses/community.js +5 -0
  231. package/dist/ui/viewer/lenses/edges.js +4 -0
  232. package/dist/ui/viewer/lenses/impact.js +6 -0
  233. package/dist/ui/viewer/lenses/search.js +69 -0
  234. package/dist/ui/viewer/lod.js +100 -0
  235. package/dist/ui/viewer/main.js +92 -0
  236. package/dist/ui/viewer/picking.js +45 -0
  237. package/dist/ui/viewer/scene.js +85 -0
  238. package/dist/ui/viewer/skins/default-skin.js +15 -0
  239. package/dist/ui/viewer/skins/loader.js +44 -0
  240. package/dist/ui/viewer/skins/manifest-schema.js +30 -0
  241. package/dist/ui/viewer/state.js +44 -0
  242. package/dist/ui/viewer/theme.js +23 -0
  243. package/dist/ui/viewer/universe.js +151 -0
  244. package/dist/ui/viewer/viewer.css +85 -0
  245. package/dist/util/safeRegex.d.ts.map +1 -1
  246. package/dist/util/safeRegex.js +221 -68
  247. package/dist/util/safeRegex.js.map +1 -1
  248. package/dist/viewer/legacy-graph.d.ts +40 -0
  249. package/dist/viewer/legacy-graph.d.ts.map +1 -0
  250. package/dist/viewer/legacy-graph.js +152 -0
  251. package/dist/viewer/legacy-graph.js.map +1 -0
  252. package/dist/viewer/routes.d.ts +10 -0
  253. package/dist/viewer/routes.d.ts.map +1 -0
  254. package/dist/viewer/routes.js +221 -0
  255. package/dist/viewer/routes.js.map +1 -0
  256. package/dist/viewer/service.d.ts +44 -0
  257. package/dist/viewer/service.d.ts.map +1 -0
  258. package/dist/viewer/service.js +160 -0
  259. package/dist/viewer/service.js.map +1 -0
  260. package/dist/viewer/types.d.ts +63 -0
  261. package/dist/viewer/types.d.ts.map +1 -0
  262. package/dist/viewer/types.js +2 -0
  263. package/dist/viewer/types.js.map +1 -0
  264. package/dist/viewer/viewer-config.d.ts +12 -0
  265. package/dist/viewer/viewer-config.d.ts.map +1 -0
  266. package/dist/viewer/viewer-config.js +36 -0
  267. package/dist/viewer/viewer-config.js.map +1 -0
  268. package/package.json +18 -9
  269. package/templates/SDL.md +19 -0
  270. package/templates/claude-code.json +8 -43
  271. package/dist/ui/graph.css +0 -108
  272. package/dist/ui/graph.d.ts +0 -2
  273. package/dist/ui/graph.d.ts.map +0 -1
  274. package/dist/ui/graph.html +0 -52
  275. package/dist/ui/graph.js +0 -259
  276. package/dist/ui/graph.js.map +0 -1
@@ -0,0 +1,1051 @@
1
+ import {
2
+ AddOperation,
3
+ BackSide,
4
+ BufferGeometry,
5
+ ClampToEdgeWrapping,
6
+ Color,
7
+ DoubleSide,
8
+ EquirectangularReflectionMapping,
9
+ EquirectangularRefractionMapping,
10
+ FileLoader,
11
+ Float32BufferAttribute,
12
+ FrontSide,
13
+ LineBasicMaterial,
14
+ LineSegments,
15
+ Loader,
16
+ Mesh,
17
+ MeshPhongMaterial,
18
+ MeshPhysicalMaterial,
19
+ MeshStandardMaterial,
20
+ MirroredRepeatWrapping,
21
+ Points,
22
+ PointsMaterial,
23
+ RepeatWrapping,
24
+ SRGBColorSpace,
25
+ TextureLoader,
26
+ Vector2,
27
+ Vector3
28
+ } from 'three';
29
+
30
+ import { IFFParser } from './lwo/IFFParser.js';
31
+
32
+ let _lwoTree;
33
+
34
+ /**
35
+ * A loader for the LWO format.
36
+ *
37
+ * LWO3 and LWO2 formats are supported.
38
+ *
39
+ * References:
40
+ * - [LWO3 format specification](https://static.lightwave3d.com/sdk/2019/html/filefmts/lwo3.html)
41
+ * - [LWO2 format specification](https://static.lightwave3d.com/sdk/2019/html/filefmts/lwo2.html)
42
+ *
43
+ * ```js
44
+ * const loader = new LWOLoader();
45
+ * const lwoData = await loader.loadAsync( 'models/lwo/Objects/LWO3/Demo.lwo' );
46
+ *
47
+ * const mesh = object.meshes[ 0 ];
48
+ * scene.add( mesh );
49
+ * ```
50
+ *
51
+ * @augments Loader
52
+ * @three_import import { LWOLoader } from 'three/addons/loaders/LWOLoader.js';
53
+ * @deprecated since r185.
54
+ */
55
+ class LWOLoader extends Loader {
56
+
57
+ /**
58
+ * Constructs a new LWO loader.
59
+ *
60
+ * @param {LoadingManager} [manager] - The loading manager.
61
+ * @deprecated since r185.
62
+ */
63
+ constructor( manager ) {
64
+
65
+ super( manager );
66
+
67
+ console.warn( 'THREE.LWOLoader: The loader has been deprecated and will be removed with r195. Export your LWO files to glTF before using them on the web.' ); // @deprecated, r185
68
+
69
+ }
70
+
71
+ /**
72
+ * Starts loading from the given URL and passes the loaded LWO asset
73
+ * to the `onLoad()` callback.
74
+ *
75
+ * @param {string} url - The path/URL of the file to be loaded. This can also be a data URI.
76
+ * @param {function({meshes:Array<Mesh>,materials:Array<Material>})} onLoad - Executed when the loading process has been finished.
77
+ * @param {onProgressCallback} onProgress - Executed while the loading is in progress.
78
+ * @param {onErrorCallback} onError - Executed when errors occur.
79
+ */
80
+ load( url, onLoad, onProgress, onError ) {
81
+
82
+ const scope = this;
83
+
84
+ const path = ( scope.path === '' ) ? extractParentUrl( url, 'Objects' ) : scope.path;
85
+
86
+ // give the mesh a default name based on the filename
87
+ const modelName = url.split( path ).pop().split( '.' )[ 0 ];
88
+
89
+ const loader = new FileLoader( this.manager );
90
+ loader.setPath( scope.path );
91
+ loader.setResponseType( 'arraybuffer' );
92
+
93
+ loader.load( url, function ( buffer ) {
94
+
95
+ // console.time( 'Total parsing: ' );
96
+
97
+ try {
98
+
99
+ onLoad( scope.parse( buffer, path, modelName ) );
100
+
101
+ } catch ( e ) {
102
+
103
+ if ( onError ) {
104
+
105
+ onError( e );
106
+
107
+ } else {
108
+
109
+ console.error( e );
110
+
111
+ }
112
+
113
+ scope.manager.itemError( url );
114
+
115
+ }
116
+
117
+ // console.timeEnd( 'Total parsing: ' );
118
+
119
+ }, onProgress, onError );
120
+
121
+ }
122
+
123
+ /**
124
+ * Parses the given LWO data and returns the resulting meshes and materials.
125
+ *
126
+ * @param {ArrayBuffer} iffBuffer - The raw LWO data as an array buffer.
127
+ * @param {string} path - The URL base path.
128
+ * @param {string} modelName - The model name.
129
+ * @return {{meshes:Array<Mesh>,materials:Array<Material>}} An object holding the parse meshes and materials.
130
+ */
131
+ parse( iffBuffer, path, modelName ) {
132
+
133
+ _lwoTree = new IFFParser().parse( iffBuffer );
134
+
135
+ // console.log( 'lwoTree', lwoTree );
136
+
137
+ const textureLoader = new TextureLoader( this.manager ).setPath( this.resourcePath || path ).setCrossOrigin( this.crossOrigin );
138
+
139
+ return new LWOTreeParser( textureLoader ).parse( modelName );
140
+
141
+ }
142
+
143
+ }
144
+
145
+ // Parse the lwoTree object
146
+ class LWOTreeParser {
147
+
148
+ constructor( textureLoader ) {
149
+
150
+ this.textureLoader = textureLoader;
151
+
152
+ }
153
+
154
+ parse( modelName ) {
155
+
156
+ this.materials = new MaterialParser( this.textureLoader ).parse();
157
+ this.defaultLayerName = modelName;
158
+
159
+ this.meshes = this.parseLayers();
160
+
161
+ return {
162
+ materials: this.materials,
163
+ meshes: this.meshes,
164
+ };
165
+
166
+ }
167
+
168
+ parseLayers() {
169
+
170
+ // array of all meshes for building hierarchy
171
+ const meshes = [];
172
+
173
+ // final array containing meshes with scene graph hierarchy set up
174
+ const finalMeshes = [];
175
+
176
+ const geometryParser = new GeometryParser();
177
+
178
+ const scope = this;
179
+ _lwoTree.layers.forEach( function ( layer ) {
180
+
181
+ const geometry = geometryParser.parse( layer.geometry, layer );
182
+
183
+ const mesh = scope.parseMesh( geometry, layer );
184
+
185
+ meshes[ layer.number ] = mesh;
186
+
187
+ if ( layer.parent === - 1 ) finalMeshes.push( mesh );
188
+ else meshes[ layer.parent ].add( mesh );
189
+
190
+
191
+ } );
192
+
193
+ return finalMeshes;
194
+
195
+ }
196
+
197
+ parseMesh( geometry, layer ) {
198
+
199
+ let mesh;
200
+
201
+ const materials = this.getMaterials( geometry.userData.matNames, layer.geometry.type );
202
+
203
+ if ( layer.geometry.type === 'points' ) mesh = new Points( geometry, materials );
204
+ else if ( layer.geometry.type === 'lines' ) mesh = new LineSegments( geometry, materials );
205
+ else mesh = new Mesh( geometry, materials );
206
+
207
+ if ( layer.name ) mesh.name = layer.name;
208
+ else mesh.name = this.defaultLayerName + '_layer_' + layer.number;
209
+
210
+ const pivot = layer.pivot;
211
+ if ( pivot[ 0 ] !== 0 || pivot[ 1 ] !== 0 || pivot[ 2 ] !== 0 ) {
212
+
213
+ mesh.pivot = new Vector3( pivot[ 0 ], pivot[ 1 ], pivot[ 2 ] );
214
+
215
+ }
216
+
217
+ return mesh;
218
+
219
+ }
220
+
221
+ getMaterials( namesArray, type ) {
222
+
223
+ const materials = [];
224
+
225
+ const scope = this;
226
+
227
+ namesArray.forEach( function ( name, i ) {
228
+
229
+ materials[ i ] = scope.getMaterialByName( name );
230
+
231
+ } );
232
+
233
+ // convert materials to line or point mats if required
234
+ if ( type === 'points' || type === 'lines' ) {
235
+
236
+ materials.forEach( function ( mat, i ) {
237
+
238
+ const spec = {
239
+ color: mat.color,
240
+ };
241
+
242
+ if ( type === 'points' ) {
243
+
244
+ spec.size = 0.1;
245
+ spec.map = mat.map;
246
+ materials[ i ] = new PointsMaterial( spec );
247
+
248
+ } else if ( type === 'lines' ) {
249
+
250
+ materials[ i ] = new LineBasicMaterial( spec );
251
+
252
+ }
253
+
254
+ } );
255
+
256
+ }
257
+
258
+ // if there is only one material, return that directly instead of array
259
+ const filtered = materials.filter( Boolean );
260
+ if ( filtered.length === 1 ) return filtered[ 0 ];
261
+
262
+ return materials;
263
+
264
+ }
265
+
266
+ getMaterialByName( name ) {
267
+
268
+ return this.materials.filter( function ( m ) {
269
+
270
+ return m.name === name;
271
+
272
+ } )[ 0 ];
273
+
274
+ }
275
+
276
+ }
277
+
278
+ class MaterialParser {
279
+
280
+ constructor( textureLoader ) {
281
+
282
+ this.textureLoader = textureLoader;
283
+
284
+ }
285
+
286
+ parse() {
287
+
288
+ const materials = [];
289
+ this.textures = {};
290
+
291
+ for ( const name in _lwoTree.materials ) {
292
+
293
+ if ( _lwoTree.format === 'LWO3' ) {
294
+
295
+ materials.push( this.parseMaterial( _lwoTree.materials[ name ], name, _lwoTree.textures ) );
296
+
297
+ } else if ( _lwoTree.format === 'LWO2' ) {
298
+
299
+ materials.push( this.parseMaterialLwo2( _lwoTree.materials[ name ], name, _lwoTree.textures ) );
300
+
301
+ }
302
+
303
+ }
304
+
305
+ return materials;
306
+
307
+ }
308
+
309
+ parseMaterial( materialData, name, textures ) {
310
+
311
+ let params = {
312
+ name: name,
313
+ side: this.getSide( materialData.attributes ),
314
+ flatShading: this.getSmooth( materialData.attributes ),
315
+ };
316
+
317
+ const connections = this.parseConnections( materialData.connections, materialData.nodes );
318
+
319
+ const maps = this.parseTextureNodes( connections.maps );
320
+
321
+ this.parseAttributeImageMaps( connections.attributes, textures, maps );
322
+
323
+ const attributes = this.parseAttributes( connections.attributes, maps );
324
+
325
+ this.parseEnvMap( connections, maps, attributes );
326
+
327
+ params = Object.assign( maps, params );
328
+ params = Object.assign( params, attributes );
329
+
330
+ const materialType = this.getMaterialType( connections.attributes );
331
+
332
+ if ( materialType !== MeshPhongMaterial ) delete params.refractionRatio; // PBR materials do not support "refractionRatio"
333
+
334
+ return new materialType( params );
335
+
336
+ }
337
+
338
+ parseMaterialLwo2( materialData, name/*, textures*/ ) {
339
+
340
+ let params = {
341
+ name: name,
342
+ side: this.getSide( materialData.attributes ),
343
+ flatShading: this.getSmooth( materialData.attributes ),
344
+ };
345
+
346
+ const attributes = this.parseAttributes( materialData.attributes, {} );
347
+ params = Object.assign( params, attributes );
348
+ return new MeshPhongMaterial( params );
349
+
350
+ }
351
+
352
+ // Note: converting from left to right handed coords by switching x -> -x in vertices, and
353
+ // then switching mat FrontSide -> BackSide
354
+ // NB: this means that FrontSide and BackSide have been switched!
355
+ getSide( attributes ) {
356
+
357
+ if ( ! attributes.side ) return BackSide;
358
+
359
+ switch ( attributes.side ) {
360
+
361
+ case 0:
362
+ case 1:
363
+ return BackSide;
364
+ case 2: return FrontSide;
365
+ case 3: return DoubleSide;
366
+
367
+ }
368
+
369
+ }
370
+
371
+ getSmooth( attributes ) {
372
+
373
+ if ( ! attributes.smooth ) return true;
374
+ return ! attributes.smooth;
375
+
376
+ }
377
+
378
+ parseConnections( connections, nodes ) {
379
+
380
+ const materialConnections = {
381
+ maps: {}
382
+ };
383
+
384
+ const inputName = connections.inputName;
385
+ const inputNodeName = connections.inputNodeName;
386
+ const nodeName = connections.nodeName;
387
+
388
+ const scope = this;
389
+ inputName.forEach( function ( name, index ) {
390
+
391
+ if ( name === 'Material' ) {
392
+
393
+ const matNode = scope.getNodeByRefName( inputNodeName[ index ], nodes );
394
+ materialConnections.attributes = matNode.attributes;
395
+ materialConnections.envMap = matNode.fileName;
396
+ materialConnections.name = inputNodeName[ index ];
397
+
398
+ }
399
+
400
+ } );
401
+
402
+ nodeName.forEach( function ( name, index ) {
403
+
404
+ if ( name === materialConnections.name ) {
405
+
406
+ materialConnections.maps[ inputName[ index ] ] = scope.getNodeByRefName( inputNodeName[ index ], nodes );
407
+
408
+ }
409
+
410
+ } );
411
+
412
+ return materialConnections;
413
+
414
+ }
415
+
416
+ getNodeByRefName( refName, nodes ) {
417
+
418
+ for ( const name in nodes ) {
419
+
420
+ if ( nodes[ name ].refName === refName ) return nodes[ name ];
421
+
422
+ }
423
+
424
+ }
425
+
426
+ parseTextureNodes( textureNodes ) {
427
+
428
+ const maps = {};
429
+
430
+ for ( const name in textureNodes ) {
431
+
432
+ const node = textureNodes[ name ];
433
+ const path = node.fileName;
434
+
435
+ if ( ! path ) return;
436
+
437
+ const texture = this.loadTexture( path );
438
+
439
+ if ( node.widthWrappingMode !== undefined ) texture.wrapS = this.getWrappingType( node.widthWrappingMode );
440
+ if ( node.heightWrappingMode !== undefined ) texture.wrapT = this.getWrappingType( node.heightWrappingMode );
441
+
442
+ switch ( name ) {
443
+
444
+ case 'Color':
445
+ maps.map = texture;
446
+ maps.map.colorSpace = SRGBColorSpace;
447
+ break;
448
+ case 'Roughness':
449
+ maps.roughnessMap = texture;
450
+ maps.roughness = 1;
451
+ break;
452
+ case 'Specular':
453
+ maps.specularMap = texture;
454
+ maps.specularMap.colorSpace = SRGBColorSpace;
455
+ maps.specular = 0xffffff;
456
+ break;
457
+ case 'Luminous':
458
+ maps.emissiveMap = texture;
459
+ maps.emissiveMap.colorSpace = SRGBColorSpace;
460
+ maps.emissive = 0x808080;
461
+ break;
462
+ case 'Luminous Color':
463
+ maps.emissive = 0x808080;
464
+ break;
465
+ case 'Metallic':
466
+ maps.metalnessMap = texture;
467
+ maps.metalness = 1;
468
+ break;
469
+ case 'Transparency':
470
+ case 'Alpha':
471
+ maps.alphaMap = texture;
472
+ maps.transparent = true;
473
+ break;
474
+ case 'Normal':
475
+ maps.normalMap = texture;
476
+ if ( node.amplitude !== undefined ) maps.normalScale = new Vector2( node.amplitude, node.amplitude );
477
+ break;
478
+ case 'Bump':
479
+ maps.bumpMap = texture;
480
+ break;
481
+
482
+ }
483
+
484
+ }
485
+
486
+ // LWO BSDF materials can have both spec and rough, but this is not valid in three
487
+ if ( maps.roughnessMap && maps.specularMap ) delete maps.specularMap;
488
+
489
+ return maps;
490
+
491
+ }
492
+
493
+ // maps can also be defined on individual material attributes, parse those here
494
+ // This occurs on Standard (Phong) surfaces
495
+ parseAttributeImageMaps( attributes, textures, maps ) {
496
+
497
+ for ( const name in attributes ) {
498
+
499
+ const attribute = attributes[ name ];
500
+
501
+ if ( attribute.maps ) {
502
+
503
+ const mapData = attribute.maps[ 0 ];
504
+
505
+ const path = this.getTexturePathByIndex( mapData.imageIndex );
506
+ if ( ! path ) return;
507
+
508
+ const texture = this.loadTexture( path );
509
+
510
+ if ( mapData.wrap !== undefined ) texture.wrapS = this.getWrappingType( mapData.wrap.w );
511
+ if ( mapData.wrap !== undefined ) texture.wrapT = this.getWrappingType( mapData.wrap.h );
512
+
513
+ switch ( name ) {
514
+
515
+ case 'Color':
516
+ maps.map = texture;
517
+ maps.map.colorSpace = SRGBColorSpace;
518
+ break;
519
+ case 'Diffuse':
520
+ maps.aoMap = texture;
521
+ break;
522
+ case 'Roughness':
523
+ maps.roughnessMap = texture;
524
+ maps.roughness = 1;
525
+ break;
526
+ case 'Specular':
527
+ maps.specularMap = texture;
528
+ maps.specularMap.colorSpace = SRGBColorSpace;
529
+ maps.specular = 0xffffff;
530
+ break;
531
+ case 'Luminosity':
532
+ maps.emissiveMap = texture;
533
+ maps.emissiveMap.colorSpace = SRGBColorSpace;
534
+ maps.emissive = 0x808080;
535
+ break;
536
+ case 'Metallic':
537
+ maps.metalnessMap = texture;
538
+ maps.metalness = 1;
539
+ break;
540
+ case 'Transparency':
541
+ case 'Alpha':
542
+ maps.alphaMap = texture;
543
+ maps.transparent = true;
544
+ break;
545
+ case 'Normal':
546
+ maps.normalMap = texture;
547
+ break;
548
+ case 'Bump':
549
+ maps.bumpMap = texture;
550
+ break;
551
+
552
+ }
553
+
554
+ }
555
+
556
+ }
557
+
558
+ }
559
+
560
+ parseAttributes( attributes, maps ) {
561
+
562
+ const params = {};
563
+
564
+ // don't use color data if color map is present
565
+ if ( attributes.Color && ! maps.map ) {
566
+
567
+ params.color = new Color().fromArray( attributes.Color.value );
568
+
569
+ } else {
570
+
571
+ params.color = new Color();
572
+
573
+ }
574
+
575
+
576
+ if ( attributes.Transparency && attributes.Transparency.value !== 0 ) {
577
+
578
+ params.opacity = 1 - attributes.Transparency.value;
579
+ params.transparent = true;
580
+
581
+ }
582
+
583
+ if ( attributes[ 'Bump Height' ] ) params.bumpScale = attributes[ 'Bump Height' ].value * 0.1;
584
+
585
+ this.parsePhysicalAttributes( params, attributes, maps );
586
+ this.parseStandardAttributes( params, attributes, maps );
587
+ this.parsePhongAttributes( params, attributes, maps );
588
+
589
+ return params;
590
+
591
+ }
592
+
593
+ parsePhysicalAttributes( params, attributes/*, maps*/ ) {
594
+
595
+ if ( attributes.Clearcoat && attributes.Clearcoat.value > 0 ) {
596
+
597
+ params.clearcoat = attributes.Clearcoat.value;
598
+
599
+ if ( attributes[ 'Clearcoat Gloss' ] ) {
600
+
601
+ params.clearcoatRoughness = 0.5 * ( 1 - attributes[ 'Clearcoat Gloss' ].value );
602
+
603
+ }
604
+
605
+ }
606
+
607
+ }
608
+
609
+ parseStandardAttributes( params, attributes, maps ) {
610
+
611
+
612
+ if ( attributes.Luminous ) {
613
+
614
+ params.emissiveIntensity = attributes.Luminous.value;
615
+
616
+ if ( attributes[ 'Luminous Color' ] && ! maps.emissive ) {
617
+
618
+ params.emissive = new Color().fromArray( attributes[ 'Luminous Color' ].value );
619
+
620
+ } else {
621
+
622
+ params.emissive = new Color( 0x808080 );
623
+
624
+ }
625
+
626
+ }
627
+
628
+ if ( attributes.Roughness && ! maps.roughnessMap ) params.roughness = attributes.Roughness.value;
629
+ if ( attributes.Metallic && ! maps.metalnessMap ) params.metalness = attributes.Metallic.value;
630
+
631
+ }
632
+
633
+ parsePhongAttributes( params, attributes, maps ) {
634
+
635
+ if ( attributes[ 'Refraction Index' ] ) params.refractionRatio = 0.98 / attributes[ 'Refraction Index' ].value;
636
+
637
+ if ( attributes.Diffuse ) params.color.multiplyScalar( attributes.Diffuse.value );
638
+
639
+ if ( attributes.Reflection ) {
640
+
641
+ params.reflectivity = attributes.Reflection.value;
642
+ params.combine = AddOperation;
643
+
644
+ }
645
+
646
+ if ( attributes.Luminosity ) {
647
+
648
+ params.emissiveIntensity = attributes.Luminosity.value;
649
+
650
+ if ( ! maps.emissiveMap && ! maps.map ) {
651
+
652
+ params.emissive = params.color;
653
+
654
+ } else {
655
+
656
+ params.emissive = new Color( 0x808080 );
657
+
658
+ }
659
+
660
+ }
661
+
662
+ // parse specular if there is no roughness - we will interpret the material as 'Phong' in this case
663
+ if ( ! attributes.Roughness && attributes.Specular && ! maps.specularMap ) {
664
+
665
+ if ( attributes[ 'Color Highlight' ] ) {
666
+
667
+ params.specular = new Color().setScalar( attributes.Specular.value ).lerp( params.color.clone().multiplyScalar( attributes.Specular.value ), attributes[ 'Color Highlight' ].value );
668
+
669
+ } else {
670
+
671
+ params.specular = new Color().setScalar( attributes.Specular.value );
672
+
673
+ }
674
+
675
+ }
676
+
677
+ if ( params.specular && attributes.Glossiness ) params.shininess = 7 + Math.pow( 2, attributes.Glossiness.value * 12 + 2 );
678
+
679
+ }
680
+
681
+ parseEnvMap( connections, maps, attributes ) {
682
+
683
+ if ( connections.envMap ) {
684
+
685
+ const envMap = this.loadTexture( connections.envMap );
686
+
687
+ if ( attributes.transparent && attributes.opacity < 0.999 ) {
688
+
689
+ envMap.mapping = EquirectangularRefractionMapping;
690
+
691
+ // Reflectivity and refraction mapping don't work well together in Phong materials
692
+ if ( attributes.reflectivity !== undefined ) {
693
+
694
+ delete attributes.reflectivity;
695
+ delete attributes.combine;
696
+
697
+ }
698
+
699
+ if ( attributes.metalness !== undefined ) {
700
+
701
+ attributes.metalness = 1; // For most transparent materials metalness should be set to 1 if not otherwise defined. If set to 0 no refraction will be visible
702
+
703
+ }
704
+
705
+ attributes.opacity = 1; // transparency fades out refraction, forcing opacity to 1 ensures a closer visual match to the material in Lightwave.
706
+
707
+ } else envMap.mapping = EquirectangularReflectionMapping;
708
+
709
+ maps.envMap = envMap;
710
+
711
+ }
712
+
713
+ }
714
+
715
+ // get texture defined at top level by its index
716
+ getTexturePathByIndex( index ) {
717
+
718
+ let fileName = '';
719
+
720
+ if ( ! _lwoTree.textures ) return fileName;
721
+
722
+ _lwoTree.textures.forEach( function ( texture ) {
723
+
724
+ if ( texture.index === index ) fileName = texture.fileName;
725
+
726
+ } );
727
+
728
+ return fileName;
729
+
730
+ }
731
+
732
+ loadTexture( path ) {
733
+
734
+ if ( ! path ) return null;
735
+
736
+ const texture = this.textureLoader.load(
737
+ path,
738
+ undefined,
739
+ undefined,
740
+ function () {
741
+
742
+ console.warn( 'LWOLoader: non-standard resource hierarchy. Use \`resourcePath\` parameter to specify root content directory.' );
743
+
744
+ }
745
+ );
746
+
747
+ return texture;
748
+
749
+ }
750
+
751
+ // 0 = Reset, 1 = Repeat, 2 = Mirror, 3 = Edge
752
+ getWrappingType( num ) {
753
+
754
+ switch ( num ) {
755
+
756
+ case 0:
757
+ console.warn( 'LWOLoader: "Reset" texture wrapping type is not supported in three.js' );
758
+ return ClampToEdgeWrapping;
759
+ case 1: return RepeatWrapping;
760
+ case 2: return MirroredRepeatWrapping;
761
+ case 3: return ClampToEdgeWrapping;
762
+
763
+ }
764
+
765
+ }
766
+
767
+ getMaterialType( nodeData ) {
768
+
769
+ if ( nodeData.Clearcoat && nodeData.Clearcoat.value > 0 ) return MeshPhysicalMaterial;
770
+ if ( nodeData.Roughness ) return MeshStandardMaterial;
771
+ return MeshPhongMaterial;
772
+
773
+ }
774
+
775
+ }
776
+
777
+ class GeometryParser {
778
+
779
+ parse( geoData, layer ) {
780
+
781
+ const geometry = new BufferGeometry();
782
+
783
+ geometry.setAttribute( 'position', new Float32BufferAttribute( geoData.points, 3 ) );
784
+
785
+ const indices = this.splitIndices( geoData.vertexIndices, geoData.polygonDimensions );
786
+ geometry.setIndex( indices );
787
+
788
+ this.parseGroups( geometry, geoData );
789
+
790
+ geometry.computeVertexNormals();
791
+
792
+ this.parseUVs( geometry, layer );
793
+ this.parseMorphTargets( geometry, layer );
794
+
795
+ return geometry;
796
+
797
+ }
798
+
799
+ // split quads into tris
800
+ splitIndices( indices, polygonDimensions ) {
801
+
802
+ const remappedIndices = [];
803
+
804
+ let i = 0;
805
+ polygonDimensions.forEach( function ( dim ) {
806
+
807
+ if ( dim < 4 ) {
808
+
809
+ for ( let k = 0; k < dim; k ++ ) remappedIndices.push( indices[ i + k ] );
810
+
811
+ } else if ( dim === 4 ) {
812
+
813
+ remappedIndices.push(
814
+ indices[ i ],
815
+ indices[ i + 1 ],
816
+ indices[ i + 2 ],
817
+
818
+ indices[ i ],
819
+ indices[ i + 2 ],
820
+ indices[ i + 3 ]
821
+
822
+ );
823
+
824
+ } else if ( dim > 4 ) {
825
+
826
+ for ( let k = 1; k < dim - 1; k ++ ) {
827
+
828
+ remappedIndices.push( indices[ i ], indices[ i + k ], indices[ i + k + 1 ] );
829
+
830
+ }
831
+
832
+ console.warn( 'LWOLoader: polygons with greater than 4 sides are not supported' );
833
+
834
+ }
835
+
836
+ i += dim;
837
+
838
+ } );
839
+
840
+ return remappedIndices;
841
+
842
+ }
843
+
844
+ // NOTE: currently ignoring poly indices and assuming that they are intelligently ordered
845
+ parseGroups( geometry, geoData ) {
846
+
847
+ const tags = _lwoTree.tags;
848
+ const matNames = [];
849
+
850
+ let elemSize = 3;
851
+ if ( geoData.type === 'lines' ) elemSize = 2;
852
+ if ( geoData.type === 'points' ) elemSize = 1;
853
+
854
+ const remappedIndices = this.splitMaterialIndices( geoData.polygonDimensions, geoData.materialIndices );
855
+
856
+ let indexNum = 0; // create new indices in numerical order
857
+ const indexPairs = {}; // original indices mapped to numerical indices
858
+
859
+ let prevMaterialIndex;
860
+ let materialIndex;
861
+
862
+ let prevStart = 0;
863
+ let currentCount = 0;
864
+
865
+ for ( let i = 0; i < remappedIndices.length; i += 2 ) {
866
+
867
+ materialIndex = remappedIndices[ i + 1 ];
868
+
869
+ if ( i === 0 ) matNames[ indexNum ] = tags[ materialIndex ];
870
+
871
+ if ( prevMaterialIndex === undefined ) prevMaterialIndex = materialIndex;
872
+
873
+ if ( materialIndex !== prevMaterialIndex ) {
874
+
875
+ let currentIndex;
876
+ if ( indexPairs[ tags[ prevMaterialIndex ] ] ) {
877
+
878
+ currentIndex = indexPairs[ tags[ prevMaterialIndex ] ];
879
+
880
+ } else {
881
+
882
+ currentIndex = indexNum;
883
+ indexPairs[ tags[ prevMaterialIndex ] ] = indexNum;
884
+ matNames[ indexNum ] = tags[ prevMaterialIndex ];
885
+ indexNum ++;
886
+
887
+ }
888
+
889
+ geometry.addGroup( prevStart, currentCount, currentIndex );
890
+
891
+ prevStart += currentCount;
892
+
893
+ prevMaterialIndex = materialIndex;
894
+ currentCount = 0;
895
+
896
+ }
897
+
898
+ currentCount += elemSize;
899
+
900
+ }
901
+
902
+ // the loop above doesn't add the last group, do that here.
903
+ if ( geometry.groups.length > 0 ) {
904
+
905
+ let currentIndex;
906
+ if ( indexPairs[ tags[ materialIndex ] ] ) {
907
+
908
+ currentIndex = indexPairs[ tags[ materialIndex ] ];
909
+
910
+ } else {
911
+
912
+ currentIndex = indexNum;
913
+ indexPairs[ tags[ materialIndex ] ] = indexNum;
914
+ matNames[ indexNum ] = tags[ materialIndex ];
915
+
916
+ }
917
+
918
+ geometry.addGroup( prevStart, currentCount, currentIndex );
919
+
920
+ }
921
+
922
+ // Mat names from TAGS chunk, used to build up an array of materials for this geometry
923
+ geometry.userData.matNames = matNames;
924
+
925
+ }
926
+
927
+ splitMaterialIndices( polygonDimensions, indices ) {
928
+
929
+ const remappedIndices = [];
930
+
931
+ polygonDimensions.forEach( function ( dim, i ) {
932
+
933
+ if ( dim <= 3 ) {
934
+
935
+ remappedIndices.push( indices[ i * 2 ], indices[ i * 2 + 1 ] );
936
+
937
+ } else if ( dim === 4 ) {
938
+
939
+ remappedIndices.push( indices[ i * 2 ], indices[ i * 2 + 1 ], indices[ i * 2 ], indices[ i * 2 + 1 ] );
940
+
941
+ } else {
942
+
943
+ // ignore > 4 for now
944
+ for ( let k = 0; k < dim - 2; k ++ ) {
945
+
946
+ remappedIndices.push( indices[ i * 2 ], indices[ i * 2 + 1 ] );
947
+
948
+ }
949
+
950
+ }
951
+
952
+ } );
953
+
954
+ return remappedIndices;
955
+
956
+ }
957
+
958
+ // UV maps:
959
+ // 1: are defined via index into an array of points, not into a geometry
960
+ // - the geometry is also defined by an index into this array, but the indexes may not match
961
+ // 2: there can be any number of UV maps for a single geometry. Here these are combined,
962
+ // with preference given to the first map encountered
963
+ // 3: UV maps can be partial - that is, defined for only a part of the geometry
964
+ // 4: UV maps can be VMAP or VMAD (discontinuous, to allow for seams). In practice, most
965
+ // UV maps are defined as partially VMAP and partially VMAD
966
+ // VMADs are currently not supported
967
+ parseUVs( geometry, layer ) {
968
+
969
+ // start by creating a UV map set to zero for the whole geometry
970
+ const remappedUVs = Array.from( Array( geometry.attributes.position.count * 2 ), function () {
971
+
972
+ return 0;
973
+
974
+ } );
975
+
976
+ for ( const name in layer.uvs ) {
977
+
978
+ const uvs = layer.uvs[ name ].uvs;
979
+ const uvIndices = layer.uvs[ name ].uvIndices;
980
+
981
+ uvIndices.forEach( function ( i, j ) {
982
+
983
+ remappedUVs[ i * 2 ] = uvs[ j * 2 ];
984
+ remappedUVs[ i * 2 + 1 ] = uvs[ j * 2 + 1 ];
985
+
986
+ } );
987
+
988
+ }
989
+
990
+ geometry.setAttribute( 'uv', new Float32BufferAttribute( remappedUVs, 2 ) );
991
+
992
+ }
993
+
994
+ parseMorphTargets( geometry, layer ) {
995
+
996
+ let num = 0;
997
+ for ( const name in layer.morphTargets ) {
998
+
999
+ const remappedPoints = geometry.attributes.position.array.slice();
1000
+
1001
+ if ( ! geometry.morphAttributes.position ) geometry.morphAttributes.position = [];
1002
+
1003
+ const morphPoints = layer.morphTargets[ name ].points;
1004
+ const morphIndices = layer.morphTargets[ name ].indices;
1005
+ const type = layer.morphTargets[ name ].type;
1006
+
1007
+ morphIndices.forEach( function ( i, j ) {
1008
+
1009
+ if ( type === 'relative' ) {
1010
+
1011
+ remappedPoints[ i * 3 ] += morphPoints[ j * 3 ];
1012
+ remappedPoints[ i * 3 + 1 ] += morphPoints[ j * 3 + 1 ];
1013
+ remappedPoints[ i * 3 + 2 ] += morphPoints[ j * 3 + 2 ];
1014
+
1015
+ } else {
1016
+
1017
+ remappedPoints[ i * 3 ] = morphPoints[ j * 3 ];
1018
+ remappedPoints[ i * 3 + 1 ] = morphPoints[ j * 3 + 1 ];
1019
+ remappedPoints[ i * 3 + 2 ] = morphPoints[ j * 3 + 2 ];
1020
+
1021
+ }
1022
+
1023
+ } );
1024
+
1025
+ geometry.morphAttributes.position[ num ] = new Float32BufferAttribute( remappedPoints, 3 );
1026
+ geometry.morphAttributes.position[ num ].name = name;
1027
+
1028
+ num ++;
1029
+
1030
+ }
1031
+
1032
+ geometry.morphTargetsRelative = false;
1033
+
1034
+ }
1035
+
1036
+ }
1037
+
1038
+
1039
+ // ************** UTILITY FUNCTIONS **************
1040
+
1041
+ function extractParentUrl( url, dir ) {
1042
+
1043
+ const index = url.indexOf( dir );
1044
+
1045
+ if ( index === - 1 ) return './';
1046
+
1047
+ return url.slice( 0, index );
1048
+
1049
+ }
1050
+
1051
+ export { LWOLoader };