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,458 @@
1
+ import * as THREE from 'three';
2
+
3
+ /**
4
+ * This class can be used to optimized scenes by converting
5
+ * individual meshes into {@link BatchedMesh}. This component
6
+ * is an experimental attempt to implement auto-batching in three.js.
7
+ *
8
+ * @three_import import { SceneOptimizer } from 'three/addons/utils/SceneOptimizer.js';
9
+ */
10
+ class SceneOptimizer {
11
+
12
+ /**
13
+ * Constructs a new scene optimizer.
14
+ *
15
+ * @param {Scene} scene - The scene to optimize.
16
+ * @param {SceneOptimizer~Options} options - The configuration options.
17
+ */
18
+ constructor( scene, options = {} ) {
19
+
20
+ this.scene = scene;
21
+ this.debug = options.debug || false;
22
+
23
+ }
24
+
25
+ _bufferToHash( buffer ) {
26
+
27
+ let hash = 0;
28
+ if ( buffer.byteLength !== 0 ) {
29
+
30
+ let uintArray;
31
+ if ( buffer.buffer ) {
32
+
33
+ uintArray = new Uint8Array(
34
+ buffer.buffer,
35
+ buffer.byteOffset,
36
+ buffer.byteLength
37
+ );
38
+
39
+ } else {
40
+
41
+ uintArray = new Uint8Array( buffer );
42
+
43
+ }
44
+
45
+ for ( let i = 0; i < buffer.byteLength; i ++ ) {
46
+
47
+ const byte = uintArray[ i ];
48
+ hash = ( hash << 5 ) - hash + byte;
49
+ hash |= 0;
50
+
51
+ }
52
+
53
+ }
54
+
55
+ return hash;
56
+
57
+ }
58
+
59
+ _getMaterialPropertiesHash( material ) {
60
+
61
+ const mapProps = [
62
+ 'map',
63
+ 'alphaMap',
64
+ 'aoMap',
65
+ 'bumpMap',
66
+ 'displacementMap',
67
+ 'emissiveMap',
68
+ 'envMap',
69
+ 'lightMap',
70
+ 'metalnessMap',
71
+ 'normalMap',
72
+ 'roughnessMap',
73
+ ];
74
+
75
+ const mapHash = mapProps
76
+ .map( ( prop ) => {
77
+
78
+ const map = material[ prop ];
79
+ if ( ! map ) return 0;
80
+ return `${map.uuid}_${map.offset.x}_${map.offset.y}_${map.repeat.x}_${map.repeat.y}_${map.rotation}`;
81
+
82
+ } )
83
+ .join( '|' );
84
+
85
+ const physicalProps = [
86
+ 'transparent',
87
+ 'opacity',
88
+ 'alphaTest',
89
+ 'alphaToCoverage',
90
+ 'side',
91
+ 'vertexColors',
92
+ 'visible',
93
+ 'blending',
94
+ 'wireframe',
95
+ 'flatShading',
96
+ 'premultipliedAlpha',
97
+ 'dithering',
98
+ 'toneMapped',
99
+ 'depthTest',
100
+ 'depthWrite',
101
+ 'metalness',
102
+ 'roughness',
103
+ 'clearcoat',
104
+ 'clearcoatRoughness',
105
+ 'sheen',
106
+ 'sheenRoughness',
107
+ 'transmission',
108
+ 'thickness',
109
+ 'attenuationDistance',
110
+ 'ior',
111
+ 'iridescence',
112
+ 'iridescenceIOR',
113
+ 'iridescenceThicknessRange',
114
+ 'reflectivity',
115
+ ]
116
+ .map( ( prop ) => {
117
+
118
+ if ( typeof material[ prop ] === 'undefined' ) return 0;
119
+ if ( material[ prop ] === null ) return 0;
120
+ return material[ prop ].toString();
121
+
122
+ } )
123
+ .join( '|' );
124
+
125
+ const emissiveHash = material.emissive ? material.emissive.getHexString() : 0;
126
+ const attenuationHash = material.attenuationColor
127
+ ? material.attenuationColor.getHexString()
128
+ : 0;
129
+ const sheenColorHash = material.sheenColor
130
+ ? material.sheenColor.getHexString()
131
+ : 0;
132
+
133
+ return [
134
+ material.type,
135
+ physicalProps,
136
+ mapHash,
137
+ emissiveHash,
138
+ attenuationHash,
139
+ sheenColorHash,
140
+ ].join( '_' );
141
+
142
+ }
143
+
144
+ _getAttributesSignature( geometry ) {
145
+
146
+ return Object.keys( geometry.attributes )
147
+ .sort()
148
+ .map( ( name ) => {
149
+
150
+ const attribute = geometry.attributes[ name ];
151
+ return `${name}_${attribute.itemSize}_${attribute.normalized}`;
152
+
153
+ } )
154
+ .join( '|' );
155
+
156
+ }
157
+
158
+ _getGeometryHash( geometry ) {
159
+
160
+ const indexHash = geometry.index
161
+ ? this._bufferToHash( geometry.index.array )
162
+ : 'noIndex';
163
+ const positionHash = this._bufferToHash( geometry.attributes.position.array );
164
+ const attributesSignature = this._getAttributesSignature( geometry );
165
+ return `${indexHash}_${positionHash}_${attributesSignature}`;
166
+
167
+ }
168
+
169
+ _getBatchKey( materialProps, attributesSignature ) {
170
+
171
+ return `${materialProps}_${attributesSignature}`;
172
+
173
+ }
174
+
175
+ _analyzeModel() {
176
+
177
+ const batchGroups = new Map();
178
+ const singleGroups = new Map();
179
+ const uniqueGeometries = new Set();
180
+
181
+ this.scene.updateMatrixWorld( true );
182
+ this.scene.traverse( ( node ) => {
183
+
184
+ if ( ! node.isMesh ) return;
185
+
186
+ const materialProps = this._getMaterialPropertiesHash( node.material );
187
+ const attributesSignature = this._getAttributesSignature( node.geometry );
188
+ const batchKey = this._getBatchKey( materialProps, attributesSignature );
189
+ const geometryHash = this._getGeometryHash( node.geometry );
190
+ uniqueGeometries.add( geometryHash );
191
+
192
+ if ( ! batchGroups.has( batchKey ) ) {
193
+
194
+ batchGroups.set( batchKey, {
195
+ meshes: [],
196
+ geometryStats: new Map(),
197
+ totalInstances: 0,
198
+ materialProps: node.material.clone(),
199
+ } );
200
+
201
+ }
202
+
203
+ const group = batchGroups.get( batchKey );
204
+ group.meshes.push( node );
205
+ group.totalInstances ++;
206
+
207
+ if ( ! group.geometryStats.has( geometryHash ) ) {
208
+
209
+ group.geometryStats.set( geometryHash, {
210
+ count: 0,
211
+ vertices: node.geometry.attributes.position.count,
212
+ indices: node.geometry.index ? node.geometry.index.count : 0,
213
+ geometry: node.geometry,
214
+ } );
215
+
216
+ }
217
+
218
+ group.geometryStats.get( geometryHash ).count ++;
219
+
220
+ } );
221
+
222
+ // Move single instance groups to singleGroups
223
+ for ( const [ batchKey, group ] of batchGroups ) {
224
+
225
+ if ( group.totalInstances === 1 ) {
226
+
227
+ singleGroups.set( batchKey, group );
228
+ batchGroups.delete( batchKey );
229
+
230
+ }
231
+
232
+ }
233
+
234
+ return { batchGroups, singleGroups, uniqueGeometries: uniqueGeometries.size };
235
+
236
+ }
237
+
238
+ _createBatchedMeshes( batchGroups ) {
239
+
240
+ const meshesToRemove = new Set();
241
+
242
+ for ( const [ , group ] of batchGroups ) {
243
+
244
+ const maxGeometries = group.totalInstances;
245
+ const maxVertices = Array.from( group.geometryStats.values() ).reduce(
246
+ ( sum, stats ) => sum + stats.vertices,
247
+ 0
248
+ );
249
+ const maxIndices = Array.from( group.geometryStats.values() ).reduce(
250
+ ( sum, stats ) => sum + stats.indices,
251
+ 0
252
+ );
253
+
254
+ const batchedMaterial = new group.materialProps.constructor( group.materialProps );
255
+
256
+ if ( batchedMaterial.color !== undefined ) {
257
+
258
+ // Reset color to white, color will be set per instance
259
+ batchedMaterial.color.set( 1, 1, 1 );
260
+
261
+ }
262
+
263
+ const batchedMesh = new THREE.BatchedMesh(
264
+ maxGeometries,
265
+ maxVertices,
266
+ maxIndices,
267
+ batchedMaterial
268
+ );
269
+
270
+ const referenceMesh = group.meshes[ 0 ];
271
+ batchedMesh.name = `${referenceMesh.name}_batch`;
272
+
273
+ const geometryIds = new Map();
274
+ const inverseParentMatrix = new THREE.Matrix4();
275
+
276
+ if ( referenceMesh.parent ) {
277
+
278
+ referenceMesh.parent.updateWorldMatrix( true, false );
279
+ inverseParentMatrix.copy( referenceMesh.parent.matrixWorld ).invert();
280
+
281
+ }
282
+
283
+ for ( const mesh of group.meshes ) {
284
+
285
+ const geometryHash = this._getGeometryHash( mesh.geometry );
286
+
287
+ if ( ! geometryIds.has( geometryHash ) ) {
288
+
289
+ geometryIds.set( geometryHash, batchedMesh.addGeometry( mesh.geometry ) );
290
+
291
+ }
292
+
293
+ const geometryId = geometryIds.get( geometryHash );
294
+ const instanceId = batchedMesh.addInstance( geometryId );
295
+
296
+ const localMatrix = new THREE.Matrix4();
297
+ mesh.updateWorldMatrix( true, false );
298
+ localMatrix.copy( mesh.matrixWorld );
299
+ if ( referenceMesh.parent ) {
300
+
301
+ localMatrix.premultiply( inverseParentMatrix );
302
+
303
+ }
304
+
305
+ batchedMesh.setMatrixAt( instanceId, localMatrix );
306
+ batchedMesh.setColorAt( instanceId, mesh.material.color );
307
+
308
+ meshesToRemove.add( mesh );
309
+
310
+ }
311
+
312
+ if ( referenceMesh.parent ) {
313
+
314
+ referenceMesh.parent.add( batchedMesh );
315
+
316
+ }
317
+
318
+ }
319
+
320
+ return meshesToRemove;
321
+
322
+ }
323
+
324
+ /**
325
+ * Removes empty nodes from all descendants of the given 3D object.
326
+ *
327
+ * @param {Object3D} object - The 3D object to process.
328
+ */
329
+ removeEmptyNodes( object ) {
330
+
331
+ const children = [ ...object.children ];
332
+
333
+ for ( const child of children ) {
334
+
335
+ this.removeEmptyNodes( child );
336
+
337
+ if ( ( child instanceof THREE.Group || child.constructor === THREE.Object3D )
338
+ && child.children.length === 0 ) {
339
+
340
+ object.remove( child );
341
+
342
+ }
343
+
344
+ }
345
+
346
+ }
347
+
348
+ /**
349
+ * Removes the given array of meshes from the scene.
350
+ *
351
+ * @param {Set<Mesh>} meshesToRemove - The meshes to remove.
352
+ */
353
+ disposeMeshes( meshesToRemove ) {
354
+
355
+ meshesToRemove.forEach( ( mesh ) => {
356
+
357
+ if ( mesh.parent ) {
358
+
359
+ mesh.parent.remove( mesh );
360
+
361
+ }
362
+
363
+ if ( mesh.geometry ) mesh.geometry.dispose();
364
+ if ( mesh.material ) {
365
+
366
+ if ( Array.isArray( mesh.material ) ) {
367
+
368
+ mesh.material.forEach( ( m ) => m.dispose() );
369
+
370
+ } else {
371
+
372
+ mesh.material.dispose();
373
+
374
+ }
375
+
376
+ }
377
+
378
+ } );
379
+
380
+ }
381
+
382
+ _logDebugInfo( stats ) {
383
+
384
+ console.group( 'Scene Optimization Results' );
385
+ console.log( `Original meshes: ${stats.originalMeshes}` );
386
+ console.log( `Batched into: ${stats.batchedMeshes} BatchedMesh` );
387
+ console.log( `Single meshes: ${stats.singleMeshes} Mesh` );
388
+ console.log( `Total draw calls: ${stats.drawCalls}` );
389
+ console.log( `Reduction Ratio: ${stats.reductionRatio}% fewer draw calls` );
390
+ console.groupEnd();
391
+
392
+ }
393
+
394
+ /**
395
+ * Performs the auto-baching by identifying groups of meshes in the scene
396
+ * that can be represented as a single {@link BatchedMesh}. The method modifies
397
+ * the scene by adding instances of `BatchedMesh` and removing the now redundant
398
+ * individual meshes.
399
+ *
400
+ * @return {Scene} The optimized scene.
401
+ */
402
+ toBatchedMesh() {
403
+
404
+ const { batchGroups, singleGroups, uniqueGeometries } = this._analyzeModel();
405
+ const meshesToRemove = this._createBatchedMeshes( batchGroups );
406
+
407
+ this.disposeMeshes( meshesToRemove );
408
+ this.removeEmptyNodes( this.scene );
409
+
410
+ if ( this.debug ) {
411
+
412
+ const totalOriginalMeshes = meshesToRemove.size + singleGroups.size;
413
+ const totalFinalMeshes = batchGroups.size + singleGroups.size;
414
+
415
+ const stats = {
416
+ originalMeshes: totalOriginalMeshes,
417
+ batchedMeshes: batchGroups.size,
418
+ singleMeshes: singleGroups.size,
419
+ drawCalls: totalFinalMeshes,
420
+ uniqueGeometries: uniqueGeometries,
421
+ reductionRatio: ( ( 1 - totalFinalMeshes / totalOriginalMeshes ) * 100 ).toFixed( 1 ),
422
+ };
423
+
424
+ this._logDebugInfo( stats );
425
+
426
+ }
427
+
428
+ return this.scene;
429
+
430
+ }
431
+
432
+ /**
433
+ * Performs the auto-instancing by identifying groups of meshes in the scene
434
+ * that can be represented as a single {@link InstancedMesh}. The method modifies
435
+ * the scene by adding instances of `InstancedMesh` and removing the now redundant
436
+ * individual meshes.
437
+ *
438
+ * This method is not yet implemented.
439
+ *
440
+ * @abstract
441
+ * @return {Scene} The optimized scene.
442
+ */
443
+ toInstancingMesh() {
444
+
445
+ throw new Error( 'THREE.SceneOptimizer: InstancedMesh optimization not implemented yet' );
446
+
447
+ }
448
+
449
+ }
450
+
451
+ /**
452
+ * Constructor options of `SceneOptimizer`.
453
+ *
454
+ * @typedef {Object} SceneOptimizer~Options
455
+ * @property {boolean} [debug=false] - Whether to enable debug mode or not.
456
+ **/
457
+
458
+ export { SceneOptimizer };