@navaramap/three 0.0.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 (350) hide show
  1. package/LICENSE +8 -0
  2. package/dist/assets/atmosphere/README.md +1 -0
  3. package/dist/assets/atmosphere/higher_order_scattering.bin +0 -0
  4. package/dist/assets/atmosphere/higher_order_scattering.exr +0 -0
  5. package/dist/assets/atmosphere/irradiance.bin +0 -0
  6. package/dist/assets/atmosphere/irradiance.exr +0 -0
  7. package/dist/assets/atmosphere/scattering.bin +0 -0
  8. package/dist/assets/atmosphere/scattering.exr +0 -0
  9. package/dist/assets/atmosphere/single_mie_scattering.bin +0 -0
  10. package/dist/assets/atmosphere/single_mie_scattering.exr +0 -0
  11. package/dist/assets/atmosphere/stars.bin +0 -0
  12. package/dist/assets/atmosphere/transmittance.bin +0 -0
  13. package/dist/assets/atmosphere/transmittance.exr +0 -0
  14. package/dist/assets/cloud/README.md +1 -0
  15. package/dist/assets/cloud/local_weather.png +0 -0
  16. package/dist/assets/cloud/shape.bin +1 -0
  17. package/dist/assets/cloud/shape_detail.bin +1 -0
  18. package/dist/assets/cloud/turbulence.png +0 -0
  19. package/dist/assets/fontWorker-CEJEfRpb.js +2 -0
  20. package/dist/assets/fontWorker-CEJEfRpb.js.map +1 -0
  21. package/dist/assets/index-BUB-ijMO.js +12 -0
  22. package/dist/assets/index-BUB-ijMO.js.map +1 -0
  23. package/dist/assets/navara_wasm_font_worker_bg-665OLxjy.wasm +0 -0
  24. package/dist/assets/navara_wasm_worker_bg-BXhKC5bE.wasm +0 -0
  25. package/dist/assets/noise/README.md +1 -0
  26. package/dist/assets/noise/stbn.bin +0 -0
  27. package/dist/assets/water/README.md +3 -0
  28. package/dist/assets/water/waternormals.jpg +0 -0
  29. package/dist/index.d.ts +10114 -0
  30. package/dist/index.js +34481 -0
  31. package/dist/index.js.map +1 -0
  32. package/dist/index.umd.cjs +6345 -0
  33. package/dist/index.umd.cjs.map +1 -0
  34. package/package.json +95 -0
  35. package/src/Color.ts +111 -0
  36. package/src/ShadowMapViewers.ts +49 -0
  37. package/src/atmosphere.test.ts +76 -0
  38. package/src/atmosphere.ts +364 -0
  39. package/src/bufferView.ts +90 -0
  40. package/src/camera.ts +182 -0
  41. package/src/concurrency.ts +15 -0
  42. package/src/constants/assets.ts +30 -0
  43. package/src/constants/decoder.ts +7 -0
  44. package/src/constants/index.ts +2 -0
  45. package/src/core/BaseDesc.ts +142 -0
  46. package/src/core/BaseHandle.ts +126 -0
  47. package/src/core/DescRegistry.ts +88 -0
  48. package/src/core/EffectDesc.ts +343 -0
  49. package/src/core/EffectDescRegistry.ts +31 -0
  50. package/src/core/InstancedMeshDesc.ts +413 -0
  51. package/src/core/LightDesc.ts +216 -0
  52. package/src/core/LightDescRegistry.ts +32 -0
  53. package/src/core/MeshDesc.ts +457 -0
  54. package/src/core/MeshDescRegistry.ts +32 -0
  55. package/src/core/MeshDescWithSelectiveEffect.ts +129 -0
  56. package/src/core/Registries.ts +59 -0
  57. package/src/core/SelectiveEffectRegistry.ts +90 -0
  58. package/src/core/ViewContext.ts +235 -0
  59. package/src/core/errors.ts +16 -0
  60. package/src/core/index.ts +50 -0
  61. package/src/device.ts +414 -0
  62. package/src/effects/CustomEffectPass.ts +28 -0
  63. package/src/effects/copy.ts +10 -0
  64. package/src/effects/effect.ts +87 -0
  65. package/src/effects/index.ts +4 -0
  66. package/src/effects/render.ts +10 -0
  67. package/src/evaluations/FeatureEvaluator.ts +529 -0
  68. package/src/evaluations/index.ts +1 -0
  69. package/src/event/HillshadeContext.test.ts +232 -0
  70. package/src/event/HillshadeContext.ts +299 -0
  71. package/src/event/context.ts +279 -0
  72. package/src/event/feature.ts +357 -0
  73. package/src/event/featureEvent.ts +81 -0
  74. package/src/event/features/billboard.ts +30 -0
  75. package/src/event/features/model.ts +192 -0
  76. package/src/event/features/modelGpuBytes.test.ts +69 -0
  77. package/src/event/features/modelGpuBytes.ts +87 -0
  78. package/src/event/features/point.ts +25 -0
  79. package/src/event/features/polygon.ts +23 -0
  80. package/src/event/features/polygonOutline.ts +19 -0
  81. package/src/event/features/polyline.ts +19 -0
  82. package/src/event/features/text.ts +71 -0
  83. package/src/event/hillshade.test.ts +842 -0
  84. package/src/event/hillshade.ts +588 -0
  85. package/src/event/index.ts +700 -0
  86. package/src/event/loaders.ts +53 -0
  87. package/src/event/tile.ts +19 -0
  88. package/src/event/worker.ts +999 -0
  89. package/src/event/workerTaskSettlement.test.ts +243 -0
  90. package/src/event/workerTaskSettlement.ts +128 -0
  91. package/src/inMemoryBufferStore.test.ts +60 -0
  92. package/src/inMemoryBufferStore.ts +67 -0
  93. package/src/index.iife.ts +11 -0
  94. package/src/index.ts +2556 -0
  95. package/src/input.ts +144 -0
  96. package/src/layer.ts +183 -0
  97. package/src/layers/effect/FinalCopyEffectDesc.ts +29 -0
  98. package/src/layers/effect/MRTPassEffectDesc.ts +105 -0
  99. package/src/layers/effect/README.md +9 -0
  100. package/src/layers/effect/SelectiveEffectDesc.ts +113 -0
  101. package/src/layers/effect/SkyEnvMapEffectDesc.ts +58 -0
  102. package/src/layers/effect/TransparentPassEffectDesc.ts +62 -0
  103. package/src/layers/effect/index.ts +5 -0
  104. package/src/layers/index.ts +1 -0
  105. package/src/layersManager.ts +63 -0
  106. package/src/light.ts +15 -0
  107. package/src/loaders/AbortableImageLoader.ts +203 -0
  108. package/src/loaders/AbortableTextureLoader.ts +74 -0
  109. package/src/loaders/index.ts +5 -0
  110. package/src/material/enhancer/DESIGN.md +300 -0
  111. package/src/material/enhancer/MaterialEnhancer.ts +112 -0
  112. package/src/material/enhancer/ShaderReplacer.test.ts +114 -0
  113. package/src/material/enhancer/ShaderReplacer.ts +97 -0
  114. package/src/material/enhancer/batchTexture.ts +8 -0
  115. package/src/material/enhancer/index.ts +46 -0
  116. package/src/material/enhancer/instancedSprite/index.ts +7 -0
  117. package/src/material/enhancer/instancedSprite/instancedSpriteBaseEnhancer/index.test.ts +214 -0
  118. package/src/material/enhancer/instancedSprite/instancedSpriteBaseEnhancer/index.ts +134 -0
  119. package/src/material/enhancer/instancedSprite/instancedSpriteBaseEnhancer/markers.ts +21 -0
  120. package/src/material/enhancer/instancedSprite/instancedSpriteBaseEnhancer/material.ts +32 -0
  121. package/src/material/enhancer/instancedSprite/instancedSpriteBaseEnhancer/mutates.test.ts +277 -0
  122. package/src/material/enhancer/instancedSprite/instancedSpriteBaseEnhancer/mutates.ts +151 -0
  123. package/src/material/enhancer/instancedSprite/instancedSpriteBaseEnhancer/shader.test.ts +114 -0
  124. package/src/material/enhancer/instancedSprite/instancedSpriteBaseEnhancer/shader.ts +49 -0
  125. package/src/material/enhancer/instancedSprite/instancedSpriteBaseEnhancer/state.test.ts +82 -0
  126. package/src/material/enhancer/instancedSprite/instancedSpriteBaseEnhancer/state.ts +80 -0
  127. package/src/material/enhancer/instancedSprite/instancedSpriteBaseEnhancer/types.ts +146 -0
  128. package/src/material/enhancer/instancedSprite/instancedSpriteMaterialEnhancer.ts +105 -0
  129. package/src/material/enhancer/model/index.ts +25 -0
  130. package/src/material/enhancer/model/modelBaseEnhancer/index.test.ts +74 -0
  131. package/src/material/enhancer/model/modelBaseEnhancer/index.ts +110 -0
  132. package/src/material/enhancer/model/modelBaseEnhancer/markers.ts +27 -0
  133. package/src/material/enhancer/model/modelBaseEnhancer/material.test.ts +66 -0
  134. package/src/material/enhancer/model/modelBaseEnhancer/material.ts +44 -0
  135. package/src/material/enhancer/model/modelBaseEnhancer/mutates.test.ts +84 -0
  136. package/src/material/enhancer/model/modelBaseEnhancer/mutates.ts +47 -0
  137. package/src/material/enhancer/model/modelBaseEnhancer/shader.test.ts +24 -0
  138. package/src/material/enhancer/model/modelBaseEnhancer/shader.ts +162 -0
  139. package/src/material/enhancer/model/modelBaseEnhancer/state.test.ts +103 -0
  140. package/src/material/enhancer/model/modelBaseEnhancer/state.ts +52 -0
  141. package/src/material/enhancer/model/modelBaseEnhancer/types.ts +89 -0
  142. package/src/material/enhancer/model/modelMaterialEnhancer.ts +91 -0
  143. package/src/material/enhancer/model/modelWaterEnhancer/index.test.ts +71 -0
  144. package/src/material/enhancer/model/modelWaterEnhancer/index.ts +175 -0
  145. package/src/material/enhancer/model/modelWaterEnhancer/material.test.ts +48 -0
  146. package/src/material/enhancer/model/modelWaterEnhancer/material.ts +25 -0
  147. package/src/material/enhancer/model/modelWaterEnhancer/mutates.test.ts +89 -0
  148. package/src/material/enhancer/model/modelWaterEnhancer/mutates.ts +89 -0
  149. package/src/material/enhancer/model/modelWaterEnhancer/shader.test.ts +34 -0
  150. package/src/material/enhancer/model/modelWaterEnhancer/shader.ts +111 -0
  151. package/src/material/enhancer/model/modelWaterEnhancer/state.test.ts +105 -0
  152. package/src/material/enhancer/model/modelWaterEnhancer/state.ts +59 -0
  153. package/src/material/enhancer/model/modelWaterEnhancer/types.ts +141 -0
  154. package/src/material/enhancer/model/pntsEnhancer/index.test.ts +96 -0
  155. package/src/material/enhancer/model/pntsEnhancer/index.ts +82 -0
  156. package/src/material/enhancer/model/pntsEnhancer/markers.ts +6 -0
  157. package/src/material/enhancer/model/pntsEnhancer/material.test.ts +37 -0
  158. package/src/material/enhancer/model/pntsEnhancer/material.ts +21 -0
  159. package/src/material/enhancer/model/pntsEnhancer/mutates.test.ts +80 -0
  160. package/src/material/enhancer/model/pntsEnhancer/mutates.ts +29 -0
  161. package/src/material/enhancer/model/pntsEnhancer/shader.test.ts +32 -0
  162. package/src/material/enhancer/model/pntsEnhancer/shader.ts +56 -0
  163. package/src/material/enhancer/model/pntsEnhancer/state.test.ts +87 -0
  164. package/src/material/enhancer/model/pntsEnhancer/state.ts +29 -0
  165. package/src/material/enhancer/model/pntsEnhancer/types.ts +38 -0
  166. package/src/material/enhancer/polygon/index.ts +22 -0
  167. package/src/material/enhancer/polygon/polygonBaseEnhancer/index.test.ts +101 -0
  168. package/src/material/enhancer/polygon/polygonBaseEnhancer/index.ts +130 -0
  169. package/src/material/enhancer/polygon/polygonBaseEnhancer/markers.ts +23 -0
  170. package/src/material/enhancer/polygon/polygonBaseEnhancer/material.test.ts +19 -0
  171. package/src/material/enhancer/polygon/polygonBaseEnhancer/material.ts +38 -0
  172. package/src/material/enhancer/polygon/polygonBaseEnhancer/mutates.test.ts +75 -0
  173. package/src/material/enhancer/polygon/polygonBaseEnhancer/mutates.ts +122 -0
  174. package/src/material/enhancer/polygon/polygonBaseEnhancer/shader.test.ts +25 -0
  175. package/src/material/enhancer/polygon/polygonBaseEnhancer/shader.ts +249 -0
  176. package/src/material/enhancer/polygon/polygonBaseEnhancer/state.test.ts +102 -0
  177. package/src/material/enhancer/polygon/polygonBaseEnhancer/state.ts +92 -0
  178. package/src/material/enhancer/polygon/polygonBaseEnhancer/types.ts +150 -0
  179. package/src/material/enhancer/polygon/polygonMaterialEnhancer.ts +97 -0
  180. package/src/material/enhancer/polygon/polygonWaterEnhancer/index.test.ts +72 -0
  181. package/src/material/enhancer/polygon/polygonWaterEnhancer/index.ts +164 -0
  182. package/src/material/enhancer/polygon/polygonWaterEnhancer/material.test.ts +48 -0
  183. package/src/material/enhancer/polygon/polygonWaterEnhancer/material.ts +35 -0
  184. package/src/material/enhancer/polygon/polygonWaterEnhancer/mutates.ts +61 -0
  185. package/src/material/enhancer/polygon/polygonWaterEnhancer/shader.test.ts +34 -0
  186. package/src/material/enhancer/polygon/polygonWaterEnhancer/shader.ts +96 -0
  187. package/src/material/enhancer/polygon/polygonWaterEnhancer/state.test.ts +98 -0
  188. package/src/material/enhancer/polygon/polygonWaterEnhancer/state.ts +57 -0
  189. package/src/material/enhancer/polygon/polygonWaterEnhancer/types.ts +115 -0
  190. package/src/material/enhancer/polyline/index.ts +7 -0
  191. package/src/material/enhancer/polyline/polylineBaseEnhancer/index.test.ts +157 -0
  192. package/src/material/enhancer/polyline/polylineBaseEnhancer/index.ts +154 -0
  193. package/src/material/enhancer/polyline/polylineBaseEnhancer/markers.ts +19 -0
  194. package/src/material/enhancer/polyline/polylineBaseEnhancer/material.test.ts +42 -0
  195. package/src/material/enhancer/polyline/polylineBaseEnhancer/material.ts +38 -0
  196. package/src/material/enhancer/polyline/polylineBaseEnhancer/mutates.test.ts +120 -0
  197. package/src/material/enhancer/polyline/polylineBaseEnhancer/mutates.ts +169 -0
  198. package/src/material/enhancer/polyline/polylineBaseEnhancer/shader.test.ts +67 -0
  199. package/src/material/enhancer/polyline/polylineBaseEnhancer/shader.ts +75 -0
  200. package/src/material/enhancer/polyline/polylineBaseEnhancer/state.test.ts +55 -0
  201. package/src/material/enhancer/polyline/polylineBaseEnhancer/state.ts +91 -0
  202. package/src/material/enhancer/polyline/polylineBaseEnhancer/types.ts +170 -0
  203. package/src/material/enhancer/polyline/polylineMaterialEnhancer.ts +102 -0
  204. package/src/material/enhancer/sdfText/index.ts +7 -0
  205. package/src/material/enhancer/sdfText/sdfTextBaseEnhancer/index.ts +125 -0
  206. package/src/material/enhancer/sdfText/sdfTextBaseEnhancer/markers.ts +19 -0
  207. package/src/material/enhancer/sdfText/sdfTextBaseEnhancer/material.test.ts +35 -0
  208. package/src/material/enhancer/sdfText/sdfTextBaseEnhancer/material.ts +32 -0
  209. package/src/material/enhancer/sdfText/sdfTextBaseEnhancer/mutates.test.ts +381 -0
  210. package/src/material/enhancer/sdfText/sdfTextBaseEnhancer/mutates.ts +232 -0
  211. package/src/material/enhancer/sdfText/sdfTextBaseEnhancer/shader.ts +36 -0
  212. package/src/material/enhancer/sdfText/sdfTextBaseEnhancer/state.test.ts +118 -0
  213. package/src/material/enhancer/sdfText/sdfTextBaseEnhancer/state.ts +107 -0
  214. package/src/material/enhancer/sdfText/sdfTextBaseEnhancer/types.ts +184 -0
  215. package/src/material/enhancer/sdfText/sdfTextMaterialEnhancer.ts +100 -0
  216. package/src/material/enhancer/shadowMap/index.ts +7 -0
  217. package/src/material/enhancer/shadowMap/shadowMapDepthEnhancer/index.ts +97 -0
  218. package/src/material/enhancer/shadowMap/shadowMapDepthEnhancer/markers.ts +10 -0
  219. package/src/material/enhancer/shadowMap/shadowMapDepthEnhancer/material.ts +31 -0
  220. package/src/material/enhancer/shadowMap/shadowMapDepthEnhancer/mutates.ts +8 -0
  221. package/src/material/enhancer/shadowMap/shadowMapDepthEnhancer/shader.test.ts +17 -0
  222. package/src/material/enhancer/shadowMap/shadowMapDepthEnhancer/shader.ts +53 -0
  223. package/src/material/enhancer/shadowMap/shadowMapDepthEnhancer/state.ts +17 -0
  224. package/src/material/enhancer/shadowMap/shadowMapDepthEnhancer/types.ts +26 -0
  225. package/src/material/enhancer/test-utils/shaderCompatibility.ts +124 -0
  226. package/src/material/enhancer/tileComposite/compose.test.ts +95 -0
  227. package/src/material/enhancer/tileComposite/compose.ts +112 -0
  228. package/src/material/enhancer/tileComposite/index.ts +23 -0
  229. package/src/material/enhancer/tileComposite/tileCompositeBaseEnhancer/index.ts +11 -0
  230. package/src/material/enhancer/tileComposite/tileCompositeBaseEnhancer/mutates.ts +132 -0
  231. package/src/material/enhancer/tileComposite/tileCompositeBaseEnhancer/shader.ts +245 -0
  232. package/src/material/enhancer/tileComposite/tileCompositeBaseEnhancer/types.ts +43 -0
  233. package/src/material/enhancer/tileComposite/tileElevationHeatmapEnhancer/index.ts +29 -0
  234. package/src/material/enhancer/tileComposite/tileElevationHeatmapEnhancer/mutates.ts +68 -0
  235. package/src/material/enhancer/tileComposite/tileElevationHeatmapEnhancer/shader.ts +24 -0
  236. package/src/material/enhancer/tileComposite/tileHillshadeEnhancer/index.ts +29 -0
  237. package/src/material/enhancer/tileComposite/tileHillshadeEnhancer/mutates.ts +34 -0
  238. package/src/material/enhancer/tileComposite/tileHillshadeEnhancer/shader.ts +36 -0
  239. package/src/material/enhancer/tileComposite/tileWaterEnhancer/index.ts +23 -0
  240. package/src/material/enhancer/tileComposite/tileWaterEnhancer/mutates.ts +23 -0
  241. package/src/material/enhancer/tileComposite/tileWaterEnhancer/shader.ts +8 -0
  242. package/src/material/enhancer/tileComposite/tileWatermaskEnhancer/index.ts +23 -0
  243. package/src/material/enhancer/tileComposite/tileWatermaskEnhancer/mutates.ts +34 -0
  244. package/src/material/enhancer/tileComposite/tileWatermaskEnhancer/shader.ts +13 -0
  245. package/src/material/enhancer/tileComposite/types.ts +58 -0
  246. package/src/material/index.ts +3 -0
  247. package/src/material/macro/index.ts +1 -0
  248. package/src/material/macro/tileShader.test.ts +136 -0
  249. package/src/material/macro/tileShader.ts +287 -0
  250. package/src/material/overrideMaterialsForMRT.ts +439 -0
  251. package/src/material/selectiveEffectMaterialSetup.test.ts +52 -0
  252. package/src/material/selectiveEffectMaterialSetup.ts +53 -0
  253. package/src/material/types/index.ts +1 -0
  254. package/src/material/types/uniform.ts +1 -0
  255. package/src/mesh/DrapedMesh.ts +101 -0
  256. package/src/mesh/batchTexture.test.ts +646 -0
  257. package/src/mesh/batchTexture.ts +437 -0
  258. package/src/mesh/batchedFeature.ts +186 -0
  259. package/src/mesh/batchedSdfText.ts +484 -0
  260. package/src/mesh/featureMesh.ts +43 -0
  261. package/src/mesh/index.ts +16 -0
  262. package/src/mesh/instanced.ts +117 -0
  263. package/src/mesh/model.ts +483 -0
  264. package/src/mesh/pickableMesh.ts +55 -0
  265. package/src/mesh/pickableMeshWrapper.ts +529 -0
  266. package/src/mesh/polygon.ts +699 -0
  267. package/src/mesh/polygonOutline.ts +393 -0
  268. package/src/mesh/polyline.ts +523 -0
  269. package/src/mesh/releaseGeometryArrays.ts +57 -0
  270. package/src/mesh/rtcRteHelper.ts +211 -0
  271. package/src/mesh/sdfText.test.ts +200 -0
  272. package/src/mesh/sdfText.ts +1019 -0
  273. package/src/mesh/sprite/billboardAtlas.test.ts +177 -0
  274. package/src/mesh/sprite/billboardAtlas.ts +210 -0
  275. package/src/mesh/sprite/billboardAtlasImageLoader.ts +29 -0
  276. package/src/mesh/sprite/index.ts +1 -0
  277. package/src/mesh/sprite/instancedSprite.ts +694 -0
  278. package/src/mesh/tile.test.ts +393 -0
  279. package/src/mesh/tile.ts +1790 -0
  280. package/src/object3DEvent.ts +6 -0
  281. package/src/orchestrators/RenderPassOrchestrator.ts +147 -0
  282. package/src/orchestrators/index.ts +1 -0
  283. package/src/passes/AllDepthCopyPass.ts +165 -0
  284. package/src/passes/CustomRenderPass.ts +279 -0
  285. package/src/passes/NormalCopyPass.ts +125 -0
  286. package/src/passes/RenderTargetCopyPass.ts +91 -0
  287. package/src/passes/SkyEnvMapPass.ts +58 -0
  288. package/src/passes/index.ts +5 -0
  289. package/src/pick/pickHelper.ts +273 -0
  290. package/src/pick/pickTerrain.ts +254 -0
  291. package/src/plugins/AttributionPlugin/AttributionPlugin.test.ts +210 -0
  292. package/src/plugins/AttributionPlugin/attribution.test.ts +225 -0
  293. package/src/plugins/AttributionPlugin/attribution.ts +355 -0
  294. package/src/plugins/AttributionPlugin/attributionStyles.ts +163 -0
  295. package/src/plugins/AttributionPlugin/index.ts +702 -0
  296. package/src/plugins/index.ts +1 -0
  297. package/src/quality.ts +1 -0
  298. package/src/renderOrder.ts +3 -0
  299. package/src/scene.test.ts +164 -0
  300. package/src/scene.ts +127 -0
  301. package/src/shaders/index.ts +1 -0
  302. package/src/solar.test.ts +81 -0
  303. package/src/solar.ts +101 -0
  304. package/src/source.ts +80 -0
  305. package/src/stats.ts +102 -0
  306. package/src/tasks/computeVertexNormalsAsync.ts +15 -0
  307. package/src/tasks/constructPolygonBatchedFeature.ts +34 -0
  308. package/src/tasks/constructPolylineBatchedFeature.ts +27 -0
  309. package/src/tasks/constructQuantizedMeshTerrainMesh.ts +24 -0
  310. package/src/tasks/constructTerrainMesh.ts +25 -0
  311. package/src/tasks/getImageDataFromBlob.ts +13 -0
  312. package/src/tasks/getImageDataFromImageBitmap.ts +12 -0
  313. package/src/tasks/parseMvtTile.ts +22 -0
  314. package/src/tasks/queueTask.ts +19 -0
  315. package/src/tasks/toCreasedNormalsAsync.ts +18 -0
  316. package/src/tasks/upsampleQuantizedMeshTerrainMesh.ts +44 -0
  317. package/src/tasks/upsampleTerrainMesh.ts +39 -0
  318. package/src/texture.ts +19 -0
  319. package/src/textures.ts +13 -0
  320. package/src/tileTexture/CompositeShader.snapshot.test.ts +59 -0
  321. package/src/tileTexture/CompositeShader.test.ts +168 -0
  322. package/src/tileTexture/CompositeShader.ts +53 -0
  323. package/src/tileTexture/SlotPlanner.test.ts +105 -0
  324. package/src/tileTexture/SlotPlanner.ts +102 -0
  325. package/src/tileTexture/TileTextureCache.test.ts +227 -0
  326. package/src/tileTexture/TileTextureCache.ts +192 -0
  327. package/src/tileTexture/TileTextureCompositor.test.ts +260 -0
  328. package/src/tileTexture/TileTextureCompositor.ts +421 -0
  329. package/src/tileTexture/__snapshots__/CompositeShader.snapshot.test.ts.snap +2649 -0
  330. package/src/tileTexture/index.ts +8 -0
  331. package/src/tileTexture/types.ts +150 -0
  332. package/src/type/index.ts +261 -0
  333. package/src/uniforms.ts +20 -0
  334. package/src/utils/arraysEqual.ts +9 -0
  335. package/src/utils/blendModes.ts +80 -0
  336. package/src/utils/geometry.ts +53 -0
  337. package/src/utils/hillshadeNormalMapGenerator.ts +193 -0
  338. package/src/utils/index.ts +8 -0
  339. package/src/utils/object.test.ts +14 -0
  340. package/src/utils/object.ts +7 -0
  341. package/src/utils/replacer.test.ts +42 -0
  342. package/src/utils/replacer.ts +89 -0
  343. package/src/utils/screen.ts +29 -0
  344. package/src/utils/textureFragmentIndex.test.ts +224 -0
  345. package/src/utils/textureFragmentIndex.ts +97 -0
  346. package/src/utils/worker.ts +3 -0
  347. package/src/vite-env.d.ts +2 -0
  348. package/src/worker/index.ts +13 -0
  349. package/src/worker/tasks/computeVertexNormals.ts +15 -0
  350. package/src/worker/tasks/toCreasedNormals.ts +22 -0
@@ -0,0 +1,694 @@
1
+ import {
2
+ PointMesh as NavaraPointMesh,
3
+ BillboardMesh as NavaraBillboardMesh,
4
+ } from "@navaramap/engine";
5
+ import { degreeToRadian } from "@navaramap/three_api";
6
+ import {
7
+ InstancedBufferAttribute,
8
+ InstancedBufferGeometry,
9
+ Mesh,
10
+ Object3D,
11
+ ShaderMaterial,
12
+ BufferAttribute,
13
+ Color,
14
+ PerspectiveCamera,
15
+ Vector2,
16
+ } from "three";
17
+ import invariant from "tiny-invariant";
18
+
19
+ import type { EventContext } from "../../event/context";
20
+ import { createInstancedSpriteMaterialEnhancer } from "../../material/enhancer";
21
+ import { PickableMesh } from "../pickableMesh";
22
+
23
+ import { BillboardAtlas, type AtlasRect } from "./billboardAtlas";
24
+ import { loadAtlasImageFromUrl } from "./billboardAtlasImageLoader";
25
+
26
+ export type InstancedSpriteOptions = {
27
+ renderOrder?: number;
28
+ ctx: EventContext;
29
+ };
30
+
31
+ type PositionsInfo = {
32
+ position:
33
+ | Float32Array<ArrayBufferLike>
34
+ | {
35
+ high: Float32Array<ArrayBufferLike>;
36
+ low: Float32Array<ArrayBufferLike>;
37
+ };
38
+ batchIDs: Float32Array<ArrayBufferLike> | null;
39
+ positionSize: number;
40
+ batchIDSize: number;
41
+ nPositions: number;
42
+ RTE: boolean;
43
+ };
44
+
45
+ /** Reusable Vector2 to avoid per-frame allocations in onBeforeRender. */
46
+ const _tmpSize = new Vector2();
47
+
48
+ // Coupled with crates/navara_feature/src/geometry/point.rs::pixel_to_world
49
+ export class InstancedSpriteMesh extends Mesh implements PickableMesh {
50
+ private _batchIdToInstance = new Map<number, number>();
51
+ private _initialColor: Color = new Color(0xffffff);
52
+ private _initialHeight = 0.0;
53
+ private _initialSize = -1.0; // Negative value indicates "use uScale" in shader
54
+ private _atlas?: BillboardAtlas;
55
+ private _defaultUrl?: string;
56
+ /** Atlas rect of the current default image; re-applied to an instance when
57
+ * its per-feature override is cleared. */
58
+ private _defaultRect?: AtlasRect;
59
+ /** Instance ids whose image was overridden per-feature; the default image
60
+ * from the material no longer applies to them. */
61
+ private _imageOverrides = new Set<number>();
62
+ /** Latest override URL requested per instance. An async pack only applies
63
+ * if it still matches, so a newer override or a clear wins over slow loads. */
64
+ private _requestedImageUrls = new Map<number, string>();
65
+ /** Forwards the atlas byte footprint to the engine's memory ledger; wired
66
+ * by the feature-added handler once the owning entity bits are known. */
67
+ private _atlasBytesReporter?: (bytes: number) => void;
68
+ private _reportedAtlasBytes = 0;
69
+ private _active = true;
70
+ readonly ctx: EventContext;
71
+ /** Material enhancer for encapsulated state management */
72
+ private _enhancedMaterial?: ReturnType<
73
+ typeof createInstancedSpriteMaterialEnhancer
74
+ >;
75
+ constructor(options: InstancedSpriteOptions) {
76
+ super();
77
+ this.renderOrder = options.renderOrder ?? this.renderOrder;
78
+ this.ctx = options.ctx;
79
+ }
80
+
81
+ setActive(active: boolean) {
82
+ this._active = active;
83
+ this.updateVisibility();
84
+ }
85
+
86
+ async _init(m: NavaraPointMesh | NavaraBillboardMesh) {
87
+ const positionsInfo = this.extractPositions(m);
88
+ if (positionsInfo === null) {
89
+ console.warn("No position data found for InstancedSpriteMesh");
90
+ return;
91
+ }
92
+
93
+ // Create Geometry
94
+ this.geometry = this._initGeometry(positionsInfo, m);
95
+
96
+ // Create Material
97
+ this.material = await this._initMaterial(positionsInfo, m);
98
+
99
+ this.frustumCulled = false; // Disable since bounding box doesn't account for instance positions
100
+ }
101
+
102
+ async _update(m: NavaraPointMesh | NavaraBillboardMesh) {
103
+ const enhancer = this.getEnhancer();
104
+ const material = this.material as ShaderMaterial;
105
+
106
+ if (material.visible !== m.material.show) {
107
+ material.visible = m.material.show ?? true;
108
+ this.updateVisibility();
109
+ }
110
+
111
+ // Update enhancer state for uniform-backed properties
112
+ enhancer.update({
113
+ base: {
114
+ scale: m.material.size ?? 100.0,
115
+ center: [m.material.center?.x ?? 0.0, m.material.center?.y ?? 0.0],
116
+ sizeInMeters: m.material.sizeInMeters ?? true,
117
+ offsetDepth: m.material.offsetDepth ?? true,
118
+ transparent: m.material.transparent ?? true,
119
+ depthTest: m.material.depthTest ?? true,
120
+ effectIdsMask:
121
+ this.ctx.viewContext.selectiveEffectRegistry?.computeMask(
122
+ m.material.effectIds ?? [],
123
+ ) ?? 0,
124
+ emissiveColor: m.material.emissiveColor ?? 0,
125
+ emissiveIntensity: m.material.emissiveIntensity ?? 0,
126
+ },
127
+ });
128
+
129
+ // Color (per-instance attribute)
130
+ if (this._initialColor.getHex() !== (m.material.color ?? 0xffffff)) {
131
+ this._initialColor.setHex(m.material.color ?? 0xffffff);
132
+ const colorAttr = this.geometry.getAttribute(
133
+ "instanceColor",
134
+ ) as InstancedBufferAttribute;
135
+ const instanceCount = colorAttr.count;
136
+ for (let i = 0; i < instanceCount; i++) {
137
+ colorAttr.setXYZ(
138
+ i,
139
+ this._initialColor.r,
140
+ this._initialColor.g,
141
+ this._initialColor.b,
142
+ );
143
+ }
144
+ colorAttr.needsUpdate = true;
145
+ }
146
+
147
+ // Height (per-instance attribute - X component of instanceParams vec4)
148
+ if (this._initialHeight !== (m.material.height ?? 0.0)) {
149
+ this._initialHeight = m.material.height ?? 0.0;
150
+ const paramsAttr = this.geometry.getAttribute(
151
+ "instanceParams",
152
+ ) as InstancedBufferAttribute;
153
+ const instanceCount = paramsAttr.count;
154
+ for (let i = 0; i < instanceCount; i++) {
155
+ paramsAttr.setX(i, m.material.height ?? 0.0);
156
+ }
157
+ paramsAttr.needsUpdate = true;
158
+ }
159
+
160
+ // Position updates (per-instance attributes)
161
+ {
162
+ const positionsInfo = this.extractPositions(m);
163
+
164
+ if (positionsInfo) {
165
+ if (positionsInfo.RTE) {
166
+ const pos = positionsInfo.position as {
167
+ high: Float32Array<ArrayBufferLike>;
168
+ low: Float32Array<ArrayBufferLike>;
169
+ };
170
+ const pLow = this.geometry.getAttribute(
171
+ "instancePositionLOW",
172
+ ) as InstancedBufferAttribute;
173
+ const pHigh = this.geometry.getAttribute(
174
+ "instancePositionHIGH",
175
+ ) as InstancedBufferAttribute;
176
+ pLow.copyArray(pos.low);
177
+ pHigh.copyArray(pos.high);
178
+ pLow.needsUpdate = true;
179
+ pHigh.needsUpdate = true;
180
+ } else {
181
+ const pos = positionsInfo.position as Float32Array<ArrayBufferLike>;
182
+ const p = this.geometry.getAttribute(
183
+ "instancePosition",
184
+ ) as InstancedBufferAttribute;
185
+ p.copyArray(pos);
186
+ p.needsUpdate = true;
187
+ }
188
+ }
189
+ }
190
+
191
+ // Billboard-specific updates
192
+ if (m instanceof NavaraBillboardMesh) {
193
+ enhancer.update({
194
+ base: { alphaTest: m.material.alphaTest ?? 0.0 },
195
+ });
196
+
197
+ if (m.material.url) {
198
+ await this._setDefaultImage(m.material.url);
199
+ }
200
+ }
201
+ }
202
+
203
+ private _initGeometry(
204
+ positionsInfo: PositionsInfo,
205
+ m: NavaraPointMesh | NavaraBillboardMesh,
206
+ ) {
207
+ invariant(positionsInfo.batchIDs, "Batch IDs not found!");
208
+
209
+ // prettier-ignore
210
+ const vertices = new Float32Array([
211
+ -0.5, -0.5, 0.0, // v0
212
+ 0.5, -0.5, 0.0, // v1
213
+ 0.5, 0.5, 0.0, // v2
214
+ -0.5, -0.5, 0.0, // v3
215
+ 0.5, 0.5, 0.0, // v4
216
+ -0.5, 0.5, 0.0, // v5
217
+ ]);
218
+
219
+ // prettier-ignore
220
+ const uvs = new Float32Array([
221
+ 0.0, 0.0, // v0
222
+ 1.0, 0.0, // v1
223
+ 1.0, 1.0, // v2
224
+ 0.0, 0.0, // v3
225
+ 1.0, 1.0, // v4
226
+ 0.0, 1.0, // v5
227
+ ]);
228
+
229
+ const instanceCount = positionsInfo.nPositions;
230
+
231
+ // Create the Instanced Mesh
232
+ // We use InstancedBufferGeometry to inject our custom attributes
233
+ const instancedGeometry = new InstancedBufferGeometry();
234
+ instancedGeometry.setAttribute(
235
+ "position",
236
+ new BufferAttribute(vertices, 3),
237
+ );
238
+ instancedGeometry.setAttribute("uv", new BufferAttribute(uvs, 2));
239
+ instancedGeometry.instanceCount = instanceCount;
240
+
241
+ // Add Custom Attributes
242
+ // instanceParams: vec4(height, size, show, opacity)
243
+ const paramsBuffer = new Float32Array(instanceCount * 4);
244
+ const colorBuffer = new Float32Array(instanceCount * 3);
245
+
246
+ this._initialColor = new Color().setHex(m.material.color ?? 0xffffff);
247
+ // instanceSize defaults to a negative value to indicate "use uScale" in the shader.
248
+ this._initialSize = -1.0;
249
+ const initialShow =
250
+ m.material.show !== undefined ? (m.material.show ? 1.0 : 0.0) : 1.0;
251
+ const initialOpacityRaw = m.material.opacity ?? 1.0;
252
+ const initialOpacity = Number.isFinite(initialOpacityRaw)
253
+ ? Math.max(0.0, Math.min(1.0, initialOpacityRaw))
254
+ : 1.0;
255
+
256
+ for (let i = 0; i < instanceCount; i++) {
257
+ paramsBuffer[i * 4 + 0] = m.material.height ?? 0.0; // height
258
+ paramsBuffer[i * 4 + 1] = this._initialSize; // size
259
+ paramsBuffer[i * 4 + 2] = initialShow; // show
260
+ paramsBuffer[i * 4 + 3] = initialOpacity; // opacity
261
+
262
+ colorBuffer[i * 3 + 0] = this._initialColor.r;
263
+ colorBuffer[i * 3 + 1] = this._initialColor.g;
264
+ colorBuffer[i * 3 + 2] = this._initialColor.b;
265
+
266
+ // Map batch ID to instance id
267
+ // assuming batch IDs are 32bit floats
268
+ const batchId = positionsInfo.batchIDs[i];
269
+ this._batchIdToInstance.set(batchId, i);
270
+ }
271
+
272
+ if (m instanceof NavaraBillboardMesh) {
273
+ // instanceUvRect: vec4(x, y, w, h) — this instance's atlas sub-rect in
274
+ // pixels. Zeroed until an image is packed; a zero-height rect samples a
275
+ // transparent texel, so instances stay invisible rather than garbled.
276
+ instancedGeometry.setAttribute(
277
+ "instanceUvRect",
278
+ new InstancedBufferAttribute(new Float32Array(instanceCount * 4), 4),
279
+ );
280
+ }
281
+
282
+ if (positionsInfo.RTE) {
283
+ const pos = positionsInfo.position as {
284
+ high: Float32Array<ArrayBufferLike>;
285
+ low: Float32Array<ArrayBufferLike>;
286
+ };
287
+ instancedGeometry.setAttribute(
288
+ "instancePositionLOW",
289
+ new InstancedBufferAttribute(pos.low, positionsInfo.positionSize),
290
+ );
291
+ instancedGeometry.setAttribute(
292
+ "instancePositionHIGH",
293
+ new InstancedBufferAttribute(pos.high, positionsInfo.positionSize),
294
+ );
295
+ } else {
296
+ const pos = positionsInfo.position as Float32Array<ArrayBufferLike>;
297
+ instancedGeometry.setAttribute(
298
+ "instancePosition",
299
+ new InstancedBufferAttribute(pos, positionsInfo.positionSize),
300
+ );
301
+ }
302
+ instancedGeometry.setAttribute(
303
+ "instanceParams",
304
+ new InstancedBufferAttribute(paramsBuffer, 4),
305
+ );
306
+ instancedGeometry.setAttribute(
307
+ "instanceColor",
308
+ new InstancedBufferAttribute(colorBuffer, 3),
309
+ );
310
+ instancedGeometry.setAttribute(
311
+ "instanceBatchID",
312
+ new InstancedBufferAttribute(
313
+ positionsInfo.batchIDs,
314
+ positionsInfo.batchIDSize,
315
+ ),
316
+ );
317
+
318
+ return instancedGeometry;
319
+ }
320
+
321
+ private async _initMaterial(
322
+ positionsInfo: PositionsInfo,
323
+ m: NavaraPointMesh | NavaraBillboardMesh,
324
+ ) {
325
+ const isBillboard = m instanceof NavaraBillboardMesh;
326
+ const material = new ShaderMaterial();
327
+
328
+ // Create enhancer
329
+ const enhancer = createInstancedSpriteMaterialEnhancer(material);
330
+ this._enhancedMaterial = enhancer;
331
+
332
+ // Mount with initial props
333
+ enhancer.mount({
334
+ base: {
335
+ useRTE: positionsInfo.RTE,
336
+ billboard: isBillboard,
337
+ scale: m.material.size ?? 100.0,
338
+ center: [m.material.center?.x ?? 0.0, m.material.center?.y ?? 0.0],
339
+ sizeInMeters: m.material.sizeInMeters ?? true,
340
+ offsetDepth: m.material.offsetDepth ?? true,
341
+ alphaTest: isBillboard ? (m.material.alphaTest ?? 0.0) : 0.0,
342
+ pickable: false,
343
+ transparent: m.material.transparent ?? true,
344
+ depthTest: m.material.depthTest ?? true,
345
+ rtcCenter: [m.transform.tx, m.transform.ty, m.transform.tz],
346
+ },
347
+ });
348
+
349
+ // Initialize uniforms early so they're available before onBeforeCompile
350
+ const mutates = enhancer.mutates();
351
+ mutates.updateUniforms(material.uniforms, enhancer.states());
352
+
353
+ // Set up onBeforeRender for per-frame updates (farPlane + RTE eye position)
354
+ material.onBeforeRender = (
355
+ _renderer,
356
+ _scene,
357
+ camera,
358
+ _geometry,
359
+ _mat,
360
+ _group,
361
+ ) => {
362
+ const pCam = camera as PerspectiveCamera;
363
+ mutates.updateFarPlane(pCam.far);
364
+ mutates.updateFovRad(degreeToRadian(pCam.fov));
365
+ mutates.updateScreenHeightPx(
366
+ _renderer.getDrawingBufferSize(_tmpSize).y / _renderer.getPixelRatio(),
367
+ );
368
+
369
+ if (positionsInfo.RTE) {
370
+ mutates.updateRteUniforms(
371
+ camera.position.x,
372
+ camera.position.y,
373
+ camera.position.z,
374
+ enhancer.states(),
375
+ );
376
+ } else {
377
+ mutates.updateRtcUniforms(camera.matrixWorldInverse, enhancer.states());
378
+ }
379
+ };
380
+
381
+ // Set custom program cache key and onBeforeCompile
382
+ material.customProgramCacheKey = enhancer.programCacheKey;
383
+ material.onBeforeCompile = enhancer.transformShader;
384
+
385
+ // Handle billboard texture
386
+ if (isBillboard && m.material.url) {
387
+ await this._setDefaultImage(m.material.url);
388
+ }
389
+
390
+ material.visible = m.material.show ?? true;
391
+ this.updateVisibility();
392
+ return material;
393
+ }
394
+
395
+ private updateVisibility() {
396
+ const material = this.material;
397
+ const materialVisible =
398
+ material instanceof ShaderMaterial ? material.visible : true;
399
+ this.visible = this._active && materialVisible;
400
+ }
401
+
402
+ private extractPositions(
403
+ m: NavaraPointMesh | NavaraBillboardMesh,
404
+ ): PositionsInfo | null {
405
+ const { buf } = this.ctx;
406
+ const g = m.geometry;
407
+
408
+ const batchIdsData = g.batch_ids;
409
+ const batchIDs = buf.removeF32(batchIdsData.data);
410
+ const batchIDSize = batchIdsData.size;
411
+
412
+ const positionData = g.position;
413
+ const position = positionData
414
+ ? buf.removeF32(positionData.data)
415
+ : undefined;
416
+
417
+ if (position && positionData) {
418
+ const positionSize = positionData.size;
419
+ const nPositions = position.length / positionSize;
420
+
421
+ return {
422
+ position,
423
+ batchIDs,
424
+ batchIDSize,
425
+ positionSize,
426
+ nPositions,
427
+ RTE: false,
428
+ };
429
+ }
430
+
431
+ const positionHighData = g.position_3d_high;
432
+ const positionLowData = g.position_3d_low;
433
+ const positionHigh = positionHighData
434
+ ? buf.removeF32(positionHighData.data)
435
+ : undefined;
436
+ const positionLow = positionLowData
437
+ ? buf.removeF32(positionLowData.data)
438
+ : undefined;
439
+
440
+ if (positionHigh && positionLow && positionHighData && positionLowData) {
441
+ const positionLowSize = positionLowData.size;
442
+ const positionHighSize = positionHighData.size;
443
+ invariant(
444
+ positionLowSize === positionHighSize,
445
+ "Position high and low size mismatch",
446
+ );
447
+
448
+ const nPositions = positionHigh.length / positionHighSize;
449
+
450
+ return {
451
+ position: { high: positionHigh, low: positionLow },
452
+ batchIDs,
453
+ batchIDSize,
454
+ positionSize: positionHighSize,
455
+ nPositions,
456
+ RTE: true,
457
+ };
458
+ }
459
+
460
+ return null;
461
+ }
462
+
463
+ private _ensureAtlas(): BillboardAtlas {
464
+ this._atlas ??= new BillboardAtlas({ loadImage: loadAtlasImageFromUrl });
465
+ return this._atlas;
466
+ }
467
+
468
+ /**
469
+ * Wire the callback that reports this mesh's atlas footprint to the
470
+ * engine's memory ledger, and immediately report the current footprint —
471
+ * the default image may have been packed during `_init`, before the
472
+ * feature-added handler could wire the reporter.
473
+ */
474
+ setAtlasBytesReporter(reporter: (bytes: number) => void): void {
475
+ this._atlasBytesReporter = reporter;
476
+ this._reportAtlasBytes();
477
+ }
478
+
479
+ /** Report the atlas footprint if it changed since the last report. Must run
480
+ * after every `pack()` — the atlas may have grown even when the pack failed
481
+ * (growth up to `maxSize` happens before "no space" is decided). */
482
+ private _reportAtlasBytes(): void {
483
+ if (!this._atlasBytesReporter) return;
484
+ const bytes = this._atlas?.byteLength ?? 0;
485
+ if (bytes === this._reportedAtlasBytes) return;
486
+ this._reportedAtlasBytes = bytes;
487
+ this._atlasBytesReporter(bytes);
488
+ }
489
+
490
+ /**
491
+ * Push the atlas texture and size to the material. The texture object is
492
+ * replaced whenever the atlas grows, so this must run after every pack().
493
+ */
494
+ private _syncAtlasUniforms(): void {
495
+ const atlas = this._atlas;
496
+ if (!atlas) return;
497
+ this.getEnhancer().update({
498
+ base: {
499
+ texture: { value: atlas.texture },
500
+ atlasSize: [atlas.size, atlas.size],
501
+ },
502
+ });
503
+ }
504
+
505
+ /**
506
+ * Load the material-level image and apply its rect to every instance that
507
+ * hasn't been overridden per-feature via setFeatureImageByBatchId.
508
+ */
509
+ private async _setDefaultImage(url: string): Promise<void> {
510
+ if (this._defaultUrl === url) return;
511
+ this._defaultUrl = url;
512
+
513
+ const rect = await this._ensureAtlas().pack(url);
514
+ this._reportAtlasBytes();
515
+ if (!rect) return;
516
+ // A newer default image won the race while this one was loading.
517
+ if (this._defaultUrl !== url) return;
518
+
519
+ this._defaultRect = rect;
520
+ this._syncAtlasUniforms();
521
+ const rectAttr = this.geometry.getAttribute(
522
+ "instanceUvRect",
523
+ ) as InstancedBufferAttribute;
524
+ for (let i = 0; i < rectAttr.count; i++) {
525
+ if (this._imageOverrides.has(i)) continue;
526
+ rectAttr.setXYZW(i, rect.x, rect.y, rect.w, rect.h);
527
+ }
528
+ rectAttr.needsUpdate = true;
529
+ }
530
+
531
+ onBeforePicking(): void {
532
+ this.getEnhancer().update({ base: { pickable: true } });
533
+ }
534
+
535
+ onAfterPicking(): void {
536
+ this.getEnhancer().update({ base: { pickable: false } });
537
+ }
538
+
539
+ getRenderable(): Object3D {
540
+ return this;
541
+ }
542
+
543
+ /**
544
+ * Get the enhancer, throwing if not initialized.
545
+ */
546
+ private getEnhancer(): NonNullable<typeof this._enhancedMaterial> {
547
+ if (!this._enhancedMaterial) {
548
+ throw new Error(
549
+ "InstancedSpriteMesh material enhancer is not initialized. This usually indicates a failure during construction or geometry/material setup.",
550
+ );
551
+ }
552
+ return this._enhancedMaterial;
553
+ }
554
+
555
+ setFeatureColorByBatchId(batchId: number, color: Color) {
556
+ const instanceId = this._batchIdToInstance.get(batchId);
557
+ if (instanceId === undefined) return;
558
+
559
+ const colorAttr = this.geometry.getAttribute(
560
+ "instanceColor",
561
+ ) as InstancedBufferAttribute;
562
+ colorAttr.setXYZ(instanceId, color.r, color.g, color.b);
563
+ colorAttr.needsUpdate = true;
564
+ }
565
+
566
+ setFeatureShowByBatchId(batchId: number, rawVisible: boolean) {
567
+ const instanceId = this._batchIdToInstance.get(batchId);
568
+ if (instanceId === undefined) return;
569
+
570
+ const paramsAttr = this.geometry.getAttribute(
571
+ "instanceParams",
572
+ ) as InstancedBufferAttribute;
573
+ paramsAttr.setZ(instanceId, rawVisible ? 1.0 : 0.0);
574
+ paramsAttr.needsUpdate = true;
575
+ }
576
+
577
+ setFeatureOpacityByBatchId(batchId: number, opacity: number) {
578
+ const instanceId = this._batchIdToInstance.get(batchId);
579
+ if (instanceId === undefined) return;
580
+
581
+ const paramsAttr = this.geometry.getAttribute(
582
+ "instanceParams",
583
+ ) as InstancedBufferAttribute;
584
+ const clampedOpacity = Number.isFinite(opacity)
585
+ ? Math.max(0.0, Math.min(1.0, opacity))
586
+ : 1.0;
587
+ paramsAttr.setW(instanceId, clampedOpacity);
588
+ paramsAttr.needsUpdate = true;
589
+ }
590
+
591
+ setFeatureHeightByBatchId(batchId: number, height: number) {
592
+ const instanceId = this._batchIdToInstance.get(batchId);
593
+ if (instanceId === undefined) return;
594
+
595
+ const paramsAttr = this.geometry.getAttribute(
596
+ "instanceParams",
597
+ ) as InstancedBufferAttribute;
598
+
599
+ const sanitizedHeight = Number.isFinite(height) ? height : 0.0;
600
+ paramsAttr.setX(instanceId, sanitizedHeight);
601
+ paramsAttr.needsUpdate = true;
602
+ }
603
+
604
+ setFeatureSizeByBatchId(batchId: number, size: number) {
605
+ const instanceId = this._batchIdToInstance.get(batchId);
606
+ if (instanceId === undefined) return;
607
+
608
+ const paramsAttr = this.geometry.getAttribute(
609
+ "instanceParams",
610
+ ) as InstancedBufferAttribute;
611
+
612
+ const sanitizedSize = Number.isFinite(size)
613
+ ? size < 0.0
614
+ ? -1.0
615
+ : size
616
+ : -1.0;
617
+ paramsAttr.setY(instanceId, sanitizedSize);
618
+ paramsAttr.needsUpdate = true;
619
+ }
620
+
621
+ /**
622
+ * Give one feature its own image, packed into this mesh's texture atlas.
623
+ * Loads are deduplicated by URL, so styling many features with few distinct
624
+ * images fetches each image once. On load failure the feature keeps its
625
+ * current (default) image. Passing a nullish `url` clears the override and
626
+ * reverts the feature to the material's default image. No-op for
627
+ * non-billboard (point) meshes.
628
+ */
629
+ async setFeatureImageByBatchId(
630
+ batchId: number,
631
+ url: string | null | undefined,
632
+ ): Promise<void> {
633
+ const instanceId = this._batchIdToInstance.get(batchId);
634
+ if (instanceId === undefined) return;
635
+
636
+ const rectAttr = this.geometry.getAttribute("instanceUvRect") as
637
+ | InstancedBufferAttribute
638
+ | undefined;
639
+ if (!rectAttr) return;
640
+
641
+ if (url == null) {
642
+ this._requestedImageUrls.delete(instanceId);
643
+ if (!this._imageOverrides.delete(instanceId)) return;
644
+ // Zero rect (invisible) until the default image finishes loading, same
645
+ // as instances that never had an override.
646
+ const rect = this._defaultRect;
647
+ rectAttr.setXYZW(
648
+ instanceId,
649
+ rect?.x ?? 0,
650
+ rect?.y ?? 0,
651
+ rect?.w ?? 0,
652
+ rect?.h ?? 0,
653
+ );
654
+ rectAttr.needsUpdate = true;
655
+ return;
656
+ }
657
+
658
+ this._requestedImageUrls.set(instanceId, url);
659
+ const rect = await this._ensureAtlas().pack(url);
660
+ this._reportAtlasBytes();
661
+ if (!rect) return;
662
+ // A newer override or a clear superseded this load while it was in flight.
663
+ if (this._requestedImageUrls.get(instanceId) !== url) return;
664
+
665
+ this._imageOverrides.add(instanceId);
666
+ this._syncAtlasUniforms();
667
+ rectAttr.setXYZW(instanceId, rect.x, rect.y, rect.w, rect.h);
668
+ rectAttr.needsUpdate = true;
669
+ }
670
+
671
+ dispose(): void {
672
+ this.geometry?.dispose();
673
+
674
+ // The material's uTexture points at the atlas texture; the atlas owns it.
675
+ this._atlas?.dispose();
676
+ this._atlas = undefined;
677
+
678
+ // Clear this mesh's atlas term from the memory ledger. When disposal was
679
+ // caused by the owning tile's eviction the entity is already gone and the
680
+ // report is a no-op; when only this feature was removed the tile survives
681
+ // and the term must not linger.
682
+ if (this._reportedAtlasBytes !== 0) {
683
+ this._reportedAtlasBytes = 0;
684
+ this._atlasBytesReporter?.(0);
685
+ }
686
+
687
+ (this.material as ShaderMaterial).dispose();
688
+
689
+ // Clear internal collections to release references
690
+ this._batchIdToInstance.clear();
691
+ this._imageOverrides.clear();
692
+ this._requestedImageUrls.clear();
693
+ }
694
+ }