@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,5 @@
1
+ export * from "./FinalCopyEffectDesc";
2
+ export * from "./MRTPassEffectDesc";
3
+ export * from "./SelectiveEffectDesc";
4
+ export * from "./SkyEnvMapEffectDesc";
5
+ export * from "./TransparentPassEffectDesc";
@@ -0,0 +1 @@
1
+ export * from "./effect";
@@ -0,0 +1,63 @@
1
+ import { EffectDesc, BaseHandle, EffectHandle } from "./core";
2
+ import { Layer, type LayerEvent } from "./layer";
3
+
4
+ export class LayersManager {
5
+ private layers = new Map<string, Layer | BaseHandle>();
6
+
7
+ add(l: Layer | BaseHandle) {
8
+ this.layers.set(l.id, l);
9
+ const deleteLayer = () => {
10
+ this.layers.delete(l.id);
11
+ };
12
+ if (l instanceof BaseHandle) {
13
+ l.on("deleted", deleteLayer);
14
+ } else {
15
+ l.on("deleted", deleteLayer);
16
+ }
17
+ }
18
+
19
+ get(id: string) {
20
+ return this.layers.get(id);
21
+ }
22
+
23
+ emitById<K extends keyof LayerEvent>(
24
+ k: K,
25
+ id: string,
26
+ ...args: Parameters<LayerEvent[K]>
27
+ ) {
28
+ const l = this.layers.get(id);
29
+ if (!l) return;
30
+ if (l instanceof Layer) {
31
+ l.emit(k, ...args);
32
+ }
33
+ }
34
+
35
+ emitAll<K extends keyof LayerEvent>(
36
+ k: K,
37
+ ...args: Parameters<LayerEvent[K]>
38
+ ) {
39
+ for (const l of this.layers.values()) {
40
+ if (l instanceof Layer) {
41
+ l.emit(k, ...args);
42
+ }
43
+ }
44
+ }
45
+
46
+ *getResourceLayers() {
47
+ for (const l of this.layers.values()) {
48
+ if (!(l instanceof Layer)) continue;
49
+ yield l;
50
+ }
51
+ }
52
+
53
+ *getEffectDescs(): Generator<EffectHandle> {
54
+ for (const handle of this.layers.values()) {
55
+ if (!(handle instanceof BaseHandle)) continue;
56
+
57
+ const desc = handle.ref;
58
+ if (!(desc instanceof EffectDesc)) continue;
59
+
60
+ yield handle as EffectHandle;
61
+ }
62
+ }
63
+ }
package/src/light.ts ADDED
@@ -0,0 +1,15 @@
1
+ import type { Vector3 } from "three";
2
+
3
+ export type Light = {
4
+ ambient?: {
5
+ enabled?: boolean;
6
+ color?: number;
7
+ intensity?: number;
8
+ };
9
+ sun?: {
10
+ enabled?: boolean;
11
+ color?: number;
12
+ position?: Vector3;
13
+ intensity?: number;
14
+ };
15
+ };
@@ -0,0 +1,203 @@
1
+ import { Loader } from "three";
2
+
3
+ function createElementNS(name: string) {
4
+ return document.createElementNS("http://www.w3.org/1999/xhtml", name);
5
+ }
6
+
7
+ /** Same shape as a fetch abort rejection: `err.name === "AbortError"`. */
8
+ function abortError() {
9
+ return new DOMException("The operation was aborted.", "AbortError");
10
+ }
11
+
12
+ /**
13
+ * Decoded tile image. `ImageBitmap` on browsers with `createImageBitmap`
14
+ * (decode happens off the main thread); `HTMLImageElement` on the fallback
15
+ * path. An `ImageBitmap` is produced with `imageOrientation: "flipY"`, so the
16
+ * consumer must upload it with `texture.flipY = false` to match the
17
+ * `<img>`-path orientation (the GL flip flag is ignored for ImageBitmap
18
+ * uploads anyway).
19
+ */
20
+ export type LoadedImage = HTMLImageElement | ImageBitmap;
21
+
22
+ // Ref: https://github.com/mrdoob/three.js/blob/beab9e845f9e5ae11d648f55b24a0e910b56a85a/src/loaders/ImageLoader.js
23
+ export class AbortableImageLoader extends Loader<LoadedImage> {
24
+ loadAsyncWithAbort(
25
+ url: string,
26
+ abort?: AbortController,
27
+ onProgress?: (event: ProgressEvent) => void,
28
+ ) {
29
+ // eslint-disable-next-line @typescript-eslint/no-this-alias
30
+ const scope = this;
31
+
32
+ return new Promise<LoadedImage>(function (resolve, reject) {
33
+ scope.load(url, resolve, onProgress, reject, abort);
34
+ });
35
+ }
36
+
37
+ load(
38
+ url: string,
39
+ onLoad: (data: LoadedImage) => void,
40
+ _onProgress?: (event: ProgressEvent) => void,
41
+ onError?: (err: unknown, isAborted?: boolean) => void,
42
+ abort?: AbortController,
43
+ timeout = 5000,
44
+ ): void {
45
+ if (this.path !== undefined) url = this.path + url;
46
+
47
+ // eslint-disable-next-line @typescript-eslint/no-this-alias
48
+ const scope = this;
49
+
50
+ let settled = false;
51
+
52
+ // Report the timeout before aborting: abort() dispatches its event
53
+ // synchronously, so the reverse order would let the fallback path's
54
+ // abort listener settle the load as AbortError first and swallow the
55
+ // TimeoutError.
56
+ const timeoutId = window.setTimeout(() => {
57
+ fail(new Error("TimeoutError"));
58
+ abort?.abort();
59
+ }, timeout);
60
+
61
+ function done(image: LoadedImage) {
62
+ if (settled) return;
63
+ settled = true;
64
+ window.clearTimeout(timeoutId);
65
+
66
+ onLoad(image);
67
+
68
+ scope.manager.itemEnd(url);
69
+ }
70
+
71
+ function fail(err: unknown, isAborted?: boolean) {
72
+ if (settled) return;
73
+ settled = true;
74
+ window.clearTimeout(timeoutId);
75
+
76
+ if (onError) onError(err, isAborted);
77
+
78
+ scope.manager.itemError(url);
79
+ scope.manager.itemEnd(url);
80
+ }
81
+
82
+ // Before any async work, so every done/fail (which call itemEnd) has a
83
+ // matching itemStart even when the fetch itself rejects or times out.
84
+ scope.manager.itemStart(url);
85
+
86
+ fetch(url, { signal: abort?.signal })
87
+ .then((r) => {
88
+ // Unlike the <img> path (which fires "error" on a 4xx/5xx), fetch
89
+ // resolves on HTTP error statuses — fail here instead of trying to
90
+ // decode the error body as an image (e.g. a 404 that returns a
91
+ // placeholder image would otherwise be shown as a real tile).
92
+ if (!r.ok) throw new Error(`HTTP ${r.status} while loading ${url}`);
93
+ return r.blob();
94
+ })
95
+ .then((blob) => {
96
+ if (settled) {
97
+ return;
98
+ }
99
+ if (abort?.signal.aborted) {
100
+ fail(abortError(), true);
101
+ return;
102
+ }
103
+
104
+ if (typeof createImageBitmap === "function") {
105
+ // Decodes off the main thread and needs no object URL.
106
+ // - flipY is baked in (see LoadedImage);
107
+ // - premultiply disabled and colorSpaceConversion "none" so the
108
+ // decoded bytes match the old <img> → texImage2D path, where
109
+ // three sets UNPACK_PREMULTIPLY_ALPHA/UNPACK_COLORSPACE_CONVERSION
110
+ // to NONE (critical for RGB-encoded elevation fragments, whose
111
+ // bytes must not be color-managed before the shader decodes them).
112
+ createImageBitmap(blob, {
113
+ imageOrientation: "flipY",
114
+ premultiplyAlpha: "none",
115
+ colorSpaceConversion: "none",
116
+ })
117
+ .then((bitmap) => {
118
+ // createImageBitmap cannot be cancelled; if the load already
119
+ // settled (abort/timeout) while it was decoding, just release
120
+ // the now-useless bitmap.
121
+ if (settled || abort?.signal.aborted) {
122
+ bitmap.close();
123
+ if (!settled) fail(abortError(), true);
124
+ return;
125
+ }
126
+ done(bitmap);
127
+ })
128
+ .catch((e) => fail(e));
129
+ // Settle promptly on abort instead of blocking itemEnd (and any
130
+ // loads gated on it) until the un-cancellable decode finishes. The
131
+ // decode still runs; its .then closes the orphaned bitmap above.
132
+ abort?.signal.addEventListener(
133
+ "abort",
134
+ () => fail(abortError(), true),
135
+ { once: true },
136
+ );
137
+ return;
138
+ }
139
+
140
+ this.loadViaImageElement(url, blob, done, fail, abort);
141
+ })
142
+ .catch((e) => {
143
+ fail(e, !e.name || e.name === "AbortError");
144
+ });
145
+ }
146
+
147
+ /**
148
+ * Fallback for browsers without `createImageBitmap`. The object URL keeps
149
+ * the blob alive until revoked, so it must be revoked on every exit path
150
+ * (load, error, abort) or each fetched tile leaks its encoded bytes for the
151
+ * page's lifetime.
152
+ */
153
+ private loadViaImageElement(
154
+ url: string,
155
+ blob: Blob,
156
+ done: (image: LoadedImage) => void,
157
+ fail: (err: unknown, isAborted?: boolean) => void,
158
+ abort?: AbortController,
159
+ ) {
160
+ if (abort?.signal.aborted) {
161
+ fail(abortError(), true);
162
+ return;
163
+ }
164
+
165
+ const objectUrl = window.URL.createObjectURL(blob);
166
+ const image = createElementNS("img") as HTMLImageElement;
167
+
168
+ function cleanup() {
169
+ window.URL.revokeObjectURL(objectUrl);
170
+ image.removeEventListener("load", onImageLoad, false);
171
+ image.removeEventListener("error", onImageError, false);
172
+ abort?.signal.removeEventListener("abort", onAbort, false);
173
+ }
174
+
175
+ function onImageLoad(this: HTMLImageElement) {
176
+ cleanup();
177
+ done(this);
178
+ }
179
+
180
+ function onImageError(event: unknown) {
181
+ cleanup();
182
+ fail(event);
183
+ }
184
+
185
+ function onAbort() {
186
+ // Abort the decode by clearing the image element.
187
+ image.src = "";
188
+ image.remove();
189
+ cleanup();
190
+ fail(abortError(), true);
191
+ }
192
+
193
+ image.addEventListener("load", onImageLoad, false);
194
+ image.addEventListener("error", onImageError, false);
195
+ abort?.signal.addEventListener("abort", onAbort, false);
196
+
197
+ if (url.slice(0, 5) !== "data:") {
198
+ if (this.crossOrigin !== undefined) image.crossOrigin = this.crossOrigin;
199
+ }
200
+
201
+ image.src = objectUrl;
202
+ }
203
+ }
@@ -0,0 +1,74 @@
1
+ import { Loader, Texture } from "three";
2
+
3
+ import { AbortableImageLoader } from "./AbortableImageLoader";
4
+
5
+ /**
6
+ * Dispose a texture and release its backing image. When the texture is backed
7
+ * by an `ImageBitmap` (the `createImageBitmap` decode path in
8
+ * AbortableImageLoader), `texture.dispose()` frees the GL texture but NOT the
9
+ * bitmap's decoded-pixel memory, which lives off-heap until `.close()` — so a
10
+ * plain dispose leaks it on every tile removal. Safe for any texture: images
11
+ * that are not ImageBitmaps (render targets, HTMLImageElement) are left alone.
12
+ */
13
+ export function disposeTexture(texture: Texture): void {
14
+ const image = texture.image as unknown;
15
+ texture.dispose();
16
+ if (typeof ImageBitmap !== "undefined" && image instanceof ImageBitmap) {
17
+ image.close();
18
+ }
19
+ }
20
+
21
+ // Ref: https://github.com/mrdoob/three.js/blob/beab9e845f9e5ae11d648f55b24a0e910b56a85a/src/loaders/TextureLoader.js
22
+ export class AbortableTextureLoader extends Loader {
23
+ loadAsyncWithAbort(
24
+ url: string,
25
+ abort?: AbortController,
26
+ onProgress?: (event: ProgressEvent) => void,
27
+ ) {
28
+ // eslint-disable-next-line @typescript-eslint/no-this-alias
29
+ const scope = this;
30
+
31
+ return new Promise<Texture>(function (resolve, reject) {
32
+ scope.load(url, resolve, onProgress, reject, abort);
33
+ });
34
+ }
35
+
36
+ load(
37
+ url: string,
38
+ onLoad: (data: Texture) => void,
39
+ onProgress?: (event: ProgressEvent) => void,
40
+ onError?: (err: unknown, isAborted?: boolean) => void,
41
+ abort?: AbortController,
42
+ ): Texture {
43
+ const texture = new Texture();
44
+
45
+ const loader = new AbortableImageLoader(this.manager);
46
+ loader.setCrossOrigin(this.crossOrigin);
47
+ loader.setPath(this.path);
48
+
49
+ loader.load(
50
+ url,
51
+ function (image) {
52
+ texture.image = image;
53
+ if (
54
+ typeof ImageBitmap !== "undefined" &&
55
+ image instanceof ImageBitmap
56
+ ) {
57
+ // The bitmap is already flipped at decode time
58
+ // (imageOrientation: "flipY" — see AbortableImageLoader).
59
+ texture.flipY = false;
60
+ }
61
+ texture.needsUpdate = true;
62
+
63
+ if (onLoad !== undefined) {
64
+ onLoad(texture);
65
+ }
66
+ },
67
+ onProgress,
68
+ onError,
69
+ abort,
70
+ );
71
+
72
+ return texture;
73
+ }
74
+ }
@@ -0,0 +1,5 @@
1
+ export { AbortableImageLoader } from "./AbortableImageLoader";
2
+ export {
3
+ AbortableTextureLoader,
4
+ disposeTexture,
5
+ } from "./AbortableTextureLoader";
@@ -0,0 +1,300 @@
1
+ # Material Enhancer Pattern
2
+
3
+ A compositional pattern for Three.js materials that separates shader logic into testable, composable units.
4
+
5
+ ## Problem
6
+
7
+ Three.js materials with multiple shader features become monolithic — all shader modifications interleaved in a single class, making code **hard to test in isolation**. You cannot unit-test one shader feature without the entire material setup, and GPU uniform objects (`{ value: T }`) are mutable shared state that easily breaks when accessed from multiple places.
8
+
9
+ ## Solution: Testable Separation of Concerns
10
+
11
+ The Material Enhancer pattern splits material logic into three layers, each testable independently:
12
+
13
+ | Layer | What it holds | Testability benefit |
14
+ | ----------- | --------------------------------------------------- | --------------------------------------------- |
15
+ | **State** | Immutable config snapshot (replaced on each update) | Assert state transitions with simple equality |
16
+ | **Refs** | Mutable `{ value: T }` uniform objects for the GPU | Hidden in closure — cannot leak into tests |
17
+ | **Mutates** | Functions that sync refs from state | Test ref-syncing logic without a real shader |
18
+
19
+ The key insight: **state and refs serve different purposes**. State is a clean, serializable snapshot for comparison and assertions. Refs are GPU implementation details that must be mutated in place. By hiding refs behind mutates functions, each layer can be tested without the others.
20
+
21
+ ## API
22
+
23
+ ```typescript
24
+ type MaterialEnhancer<M, Props, States, Mutates, Shaders> = {
25
+ material: M;
26
+ availableShaders: Shaders;
27
+ mount(props: Props): void; // Initialize state + refs
28
+ update(props: Props): void; // Update state, sync refs (no recompilation)
29
+ transformShader(shader): void; // Inject custom GLSL + assign uniforms
30
+ states(): States; // Read current state (for assertions)
31
+ mutates(): Mutates; // Access controlled mutation functions
32
+ programCacheKey(): string; // Cache key for shader define changes
33
+ };
34
+ ```
35
+
36
+ ### Lifecycle
37
+
38
+ ```
39
+ 1. Create material + enhancer
40
+ 2. mount(props) → initializes state and refs
41
+ 3. material.onBeforeCompile = enhancer.transformShader
42
+ 4. Three.js calls transformShader() → injects GLSL, assigns uniforms
43
+ 5. update(props) → updates state, syncs refs (no recompilation)
44
+ ```
45
+
46
+ **Constraint**: All methods except `mount()` throw if called before `mount()` (enforced with `invariant`). The factory returns an uninitialized enhancer.
47
+
48
+ **Note**: `states()` returns the current internal state object. After calling `update()`, call `states()` again to get the latest snapshot.
49
+
50
+ ## Factory Pattern
51
+
52
+ Enhancers use factory functions. State and refs are closure-private:
53
+
54
+ ```typescript
55
+ function createFeatureEnhancer(material: MeshLambertMaterial) {
56
+ let state: FeatureState | null = null;
57
+ let mutates: FeatureMutates | null = null;
58
+
59
+ return {
60
+ material,
61
+ availableShaders: ["lambert"] as const,
62
+
63
+ mount: (props) => {
64
+ state = {
65
+ featureEnabled: props.featureEnabled ?? false,
66
+ intensity: props.intensity ?? 1.0,
67
+ };
68
+ mutates = createMutates(); // refs created inside, captured in closure
69
+ mutates.update(state);
70
+ },
71
+
72
+ update: (props) => {
73
+ const prev = state.featureEnabled;
74
+ state = { ...state, ...props };
75
+ mutates.update(state); // sync refs from new state
76
+ if (state.featureEnabled !== prev) material.needsUpdate = true;
77
+ },
78
+
79
+ transformShader: (shader) => {
80
+ if (state.featureEnabled) shader.defines.FEATURE_X = 1;
81
+ mutates.updateUniforms(shader.uniforms, state);
82
+ },
83
+
84
+ states: () => state,
85
+ mutates: () => mutates,
86
+ programCacheKey: () =>
87
+ JSON.stringify({ featureEnabled: state?.featureEnabled }),
88
+ };
89
+ }
90
+ ```
91
+
92
+ ### Refs Management (inside `createMutates`)
93
+
94
+ ```typescript
95
+ const createMutates = (): FeatureMutates => {
96
+ const refs = { uIntensity: { value: 1.0 } }; // private to closure
97
+ return {
98
+ update: (state) => {
99
+ refs.uIntensity.value = state.intensity;
100
+ },
101
+ updateUniforms: (uniforms, state) => {
102
+ uniforms.uIntensity = refs.uIntensity;
103
+ },
104
+ };
105
+ };
106
+ ```
107
+
108
+ This is the core testability mechanism. Each module can be tested in isolation:
109
+
110
+ ```typescript
111
+ // Test state transitions (state.test.ts)
112
+ const state = updateState({ intensity: 0.5 }, DEFAULT_STATE);
113
+ expect(state.intensity).toBe(0.5);
114
+
115
+ // Test ref syncing (mutates.test.ts)
116
+ const mutates = createMutates();
117
+ const state = { intensity: 0.7 };
118
+ mutates.update(state);
119
+ const uniforms: Record<string, unknown> = {};
120
+ mutates.updateUniforms(uniforms, state);
121
+ expect((uniforms.uIntensity as { value: number }).value).toBe(0.7);
122
+
123
+ // Test shader injection (shader.test.ts)
124
+ const mockShader = {
125
+ defines: {},
126
+ uniforms: {},
127
+ vertexShader: "...",
128
+ fragmentShader: "...",
129
+ };
130
+ transformShader(state, mutates, mockShader);
131
+ expect(mockShader.defines.FEATURE_X).toBe(1);
132
+ ```
133
+
134
+ Each file (`state.ts`, `mutates.ts`, `shader.ts`) has a co-located `.test.ts` file.
135
+
136
+ ### Shader Compatibility Testing
137
+
138
+ Three.js has some built-in shader types, each with different structure. An enhancer declares which shaders it supports via `availableShaders`. The shared test utility `testShaderCompatibility` automatically generates a test matrix across all supported shaders and prop configurations:
139
+
140
+ ```typescript
141
+ // shader.test.ts
142
+ testShaderCompatibility(
143
+ "featureEnhancer",
144
+ (material) => createFeatureEnhancer(material as SupportedMaterial),
145
+ [
146
+ { name: "Feature enabled", props: { featureEnabled: true } },
147
+ { name: "Feature disabled", props: { featureEnabled: false } },
148
+ ],
149
+ FEATURE_SHADER_MARKERS,
150
+ );
151
+ ```
152
+
153
+ For each (shader type x props) combination, `testShaderCompatibility` verifies:
154
+
155
+ 1. `mount(props)` + `transformShader()` does not throw
156
+ 2. `update(props)` + `transformShader()` does not throw
157
+ 3. All declared markers are present in the transformed shader output
158
+
159
+ This ensures enhancers work correctly across all Three.js shader types without writing repetitive per-shader tests.
160
+
161
+ ## Composition
162
+
163
+ Enhancers compose by wrapping a base enhancer. Each layer delegates to its base:
164
+
165
+ ```typescript
166
+ function createComposedEnhancer(baseEnhancer) {
167
+ let state = null,
168
+ mutates = null;
169
+ return {
170
+ mount: (props) => {
171
+ baseEnhancer.mount(props.base ?? {}); // delegate to base
172
+ state = createInitialState(props.feature);
173
+ mutates = createComposedMutates();
174
+ mutates.update(state);
175
+ },
176
+ transformShader: (shader) => {
177
+ baseEnhancer.transformShader(shader); // base injects first
178
+ if (state.featureEnabled) shader.defines.FEATURE_Y = 1;
179
+ mutates.updateUniforms(shader.uniforms, state);
180
+ },
181
+ update: (props) => {
182
+ if (props.base) baseEnhancer.update(props.base);
183
+ if (props.feature) {
184
+ const prev = state.featureEnabled;
185
+ state = updateState(props.feature, state);
186
+ mutates.update(state);
187
+ if (state.featureEnabled !== prev) material.needsUpdate = true;
188
+ }
189
+ },
190
+ states: () => ({ base: baseEnhancer.states(), feature: state }),
191
+ mutates: () => ({ base: baseEnhancer.mutates() }),
192
+ programCacheKey: () =>
193
+ baseEnhancer.programCacheKey() +
194
+ JSON.stringify({ featureEnabled: state?.featureEnabled }),
195
+ };
196
+ }
197
+ ```
198
+
199
+ ## Shader Modification: Replacer vs ShaderReplacer
200
+
201
+ Two utilities handle shader string manipulation. They serve different roles in the composition chain:
202
+
203
+ | Aspect | `Replacer` | `ShaderReplacer<Markers>` |
204
+ | --------------------- | ------------------------------------- | --------------------------------------------------- |
205
+ | **Used by** | Base enhancer only | Composing enhancers only |
206
+ | **Search by** | Arbitrary string / regex | Declared markers only (type-safe) |
207
+ | **Operations** | `replace()`, `replaceWithCondition()` | `insertBefore()`, `insertAfter()`, `replaceBlock()` |
208
+ | **Preserves markers** | N/A | Yes — markers remain after every operation |
209
+
210
+ ### Base enhancer: `Replacer`
211
+
212
+ The base enhancer is the first in the chain, so there are no markers yet. It uses `Replacer` to replace Three.js `#include` directives with custom GLSL and **places markers** at insertion points for composing enhancers:
213
+
214
+ ```typescript
215
+ // Base enhancer's shader.ts — uses Replacer to build initial shader + place markers
216
+ shader.fragmentShader = createReplacer(shader.fragmentShader).replace(
217
+ "uniform vec3 diffuse;",
218
+ `${MARKERS.fragment.UNIFORM_START}
219
+ uniform vec3 diffuse;
220
+ uniform bool uClampToGround;
221
+ ${MARKERS.fragment.UNIFORM_END}`,
222
+ ).source;
223
+ ```
224
+
225
+ ### Composing enhancer: `ShaderReplacer`
226
+
227
+ Composing enhancers operate on the base enhancer's output, which already contains markers. `ShaderReplacer<Markers>` restricts operations to declared markers at the type level, preventing accidental modifications to arbitrary shader code:
228
+
229
+ ```typescript
230
+ // Composing enhancer's shader.ts — uses ShaderReplacer, restricted to base markers
231
+ shader.fragmentShader = createFeatureShaderReplacer(shader.fragmentShader)
232
+ .insertAfter(MARKERS.fragment.UNIFORM_END, "uniform float uFoo;")
233
+ .replaceBlock(
234
+ {
235
+ start: MARKERS.fragment.UNIFORM_START,
236
+ end: MARKERS.fragment.UNIFORM_END,
237
+ },
238
+ "uniform float uBar;",
239
+ ).source;
240
+ ```
241
+
242
+ ### Markers
243
+
244
+ Markers are GLSL comments defined in `markers.ts` and organized by shader stage:
245
+
246
+ ```typescript
247
+ const MARKERS = {
248
+ vertex: {},
249
+ fragment: {
250
+ UNIFORM_START: "// NVR_FEATURE_UNIFORM_START",
251
+ UNIFORM_END: "// NVR_FEATURE_UNIFORM_END",
252
+ },
253
+ } as const satisfies ShaderMarkers;
254
+ ```
255
+
256
+ Each base enhancer exposes a typed factory (e.g., `createFeatureShaderReplacer`) that returns a `ShaderReplacer` restricted to its markers. All `ShaderReplacer` operations preserve markers and return `this` for chaining.
257
+
258
+ ## External Refs
259
+
260
+ Some uniforms (e.g., shared textures) are created externally and passed via props. The enhancer must hold the same `{ value: T }` object reference to receive future external mutations:
261
+
262
+ ```typescript
263
+ setTexture: (texture: UniformValue<Texture | null>) => {
264
+ refs.uTexture = texture; // assign the ref object, not just the value
265
+ },
266
+ ```
267
+
268
+ ## Shader Defines and Recompilation
269
+
270
+ State properties that affect `#define` flags follow two patterns:
271
+
272
+ - **Immutable after mount**: Cannot change after initialization (e.g., `useRTE`).
273
+ - **Mutable with recompilation**: Can change; triggers `material.needsUpdate = true` and must be included in `programCacheKey()`.
274
+
275
+ ## Type Conventions
276
+
277
+ Each enhancer defines four types:
278
+
279
+ ```typescript
280
+ type FeatureProps = { ... }; // Input (public)
281
+ type FeatureState = Readonly<{ ... }>; // Immutable snapshot (public)
282
+ type FeatureRefs = { ... }; // Uniform references (internal)
283
+ type FeatureUniforms = Partial<FeatureRefs>; // Uniform types (internal)
284
+ type FeatureMutates = Mutates<FeatureState, FeatureUniforms, { ... }>; // Mutation interface (public)
285
+ ```
286
+
287
+ ## File Organization
288
+
289
+ ```
290
+ /featureEnhancer
291
+ index.ts - Factory function + public type re-exports
292
+ types.ts - Props, State, Refs, Uniforms, Mutates type definitions
293
+ mutates.ts - createMutates, default ref values (use structuredClone for defaults)
294
+ state.ts - State transitions and default values
295
+ shader.ts - transformShader logic
296
+ material.ts - Supported material types and shader compatibility
297
+ markers.ts - Marker constants and typed ShaderReplacer factory
298
+ ```
299
+
300
+ Each module has a co-located `*.test.ts` file (e.g., `state.test.ts`, `mutates.test.ts`, `shader.test.ts`) if it has a logic.