@driftengine/core 4.4.2 → 4.6.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 (339) hide show
  1. package/CHANGELOG.json +97 -0
  2. package/README.md +1 -1
  3. package/dist/geometry/boxCsg.d.ts +28 -0
  4. package/dist/geometry/boxCsg.js +139 -0
  5. package/dist/geometry/csg.d.ts +36 -0
  6. package/dist/geometry/csg.js +329 -0
  7. package/dist/geometry/meshBuilder.js +13 -0
  8. package/dist/geometry/meshConcat.js +1 -0
  9. package/dist/geometry/pixelFont.js +2 -0
  10. package/dist/geometry/solid.d.ts +61 -0
  11. package/dist/geometry/solid.js +152 -0
  12. package/dist/geometry/solidFlat.d.ts +35 -0
  13. package/dist/geometry/solidFlat.js +428 -0
  14. package/dist/geometry/solidHarness.d.ts +19 -0
  15. package/dist/geometry/solidHarness.js +66 -0
  16. package/dist/geometry/solidNormals.d.ts +17 -0
  17. package/dist/geometry/solidNormals.js +86 -0
  18. package/dist/geometry/solidRound.d.ts +41 -0
  19. package/dist/geometry/solidRound.js +323 -0
  20. package/dist/geometry/solidWriter.d.ts +16 -0
  21. package/dist/geometry/solidWriter.js +42 -0
  22. package/dist/index.d.ts +28 -3
  23. package/dist/index.js +20 -1
  24. package/dist/render/backend/api.d.ts +34 -14
  25. package/dist/render/backend/drawFog.d.ts +26 -0
  26. package/dist/render/backend/drawFog.js +30 -0
  27. package/dist/render/backend/materialChanges.d.ts +2 -1
  28. package/dist/render/backend/materialChanges.js +6 -1
  29. package/dist/render/backend/webgl2/glassTintPrograms.d.ts +13 -0
  30. package/dist/render/backend/webgl2/glassTintPrograms.js +38 -0
  31. package/dist/render/backend/webgl2/instanced.d.ts +24 -5
  32. package/dist/render/backend/webgl2/instanced.js +72 -15
  33. package/dist/render/backend/webgl2/renderer.d.ts +201 -16
  34. package/dist/render/backend/webgl2/renderer.js +596 -224
  35. package/dist/render/backend/webgpu/buffers.d.ts +29 -1
  36. package/dist/render/backend/webgpu/buffers.js +61 -4
  37. package/dist/render/backend/webgpu/depthPass.d.ts +1 -1
  38. package/dist/render/backend/webgpu/depthPass.js +7 -2
  39. package/dist/render/backend/webgpu/drawCommand.d.ts +5 -0
  40. package/dist/render/backend/webgpu/drawCommand.js +1 -0
  41. package/dist/render/backend/webgpu/flatPass.d.ts +6 -1
  42. package/dist/render/backend/webgpu/flatPass.js +57 -9
  43. package/dist/render/backend/webgpu/glassTintPass.d.ts +28 -0
  44. package/dist/render/backend/webgpu/glassTintPass.js +206 -0
  45. package/dist/render/backend/webgpu/gpuDrivenPass.d.ts +60 -1
  46. package/dist/render/backend/webgpu/gpuDrivenPass.js +305 -5
  47. package/dist/render/backend/webgpu/instanceCullPass.d.ts +34 -0
  48. package/dist/render/backend/webgpu/instanceCullPass.js +144 -0
  49. package/dist/render/backend/webgpu/instanced.d.ts +23 -2
  50. package/dist/render/backend/webgpu/instanced.js +41 -4
  51. package/dist/render/backend/webgpu/latePass.d.ts +30 -0
  52. package/dist/render/backend/webgpu/latePass.js +140 -0
  53. package/dist/render/backend/webgpu/lightVolumePass.js +5 -1
  54. package/dist/render/backend/webgpu/motionPass.d.ts +95 -0
  55. package/dist/render/backend/webgpu/motionPass.js +491 -0
  56. package/dist/render/backend/webgpu/pipelineCache.d.ts +10 -1
  57. package/dist/render/backend/webgpu/pipelineCache.js +11 -1
  58. package/dist/render/backend/webgpu/pointGlassTintPass.d.ts +32 -0
  59. package/dist/render/backend/webgpu/pointGlassTintPass.js +193 -0
  60. package/dist/render/backend/webgpu/pointShadowPass.d.ts +35 -6
  61. package/dist/render/backend/webgpu/pointShadowPass.js +105 -20
  62. package/dist/render/backend/webgpu/postPass.d.ts +3 -13
  63. package/dist/render/backend/webgpu/renderer.d.ts +271 -64
  64. package/dist/render/backend/webgpu/renderer.js +1395 -580
  65. package/dist/render/backend/webgpu/scatterPass.js +2 -1
  66. package/dist/render/backend/webgpu/sdfTextPass.js +6 -1
  67. package/dist/render/backend/webgpu/shaders/instanceCull.wgsl.d.ts +18 -0
  68. package/dist/render/backend/webgpu/shaders/instanceCull.wgsl.js +59 -0
  69. package/dist/render/backend/webgpu/skinPaletteRing.d.ts +4 -0
  70. package/dist/render/backend/webgpu/skinPaletteRing.js +4 -0
  71. package/dist/render/backend/webgpu/snapshotMips.d.ts +29 -0
  72. package/dist/render/backend/webgpu/snapshotMips.js +52 -0
  73. package/dist/render/backend/webgpu/surfaceTexturePass.d.ts +22 -3
  74. package/dist/render/backend/webgpu/surfaceTexturePass.js +78 -39
  75. package/dist/render/driftLight/bake.d.ts +7 -0
  76. package/dist/render/driftLight/bake.js +61 -56
  77. package/dist/render/driftLight/denseField.d.ts +46 -0
  78. package/dist/render/driftLight/denseField.js +109 -0
  79. package/dist/render/driftLight/lightField.d.ts +10 -5
  80. package/dist/render/driftLight/lightField.js +25 -19
  81. package/dist/render/driftLight/presence.d.ts +50 -0
  82. package/dist/render/driftLight/presence.js +43 -0
  83. package/dist/render/driftLight/uniforms.d.ts +3 -3
  84. package/dist/render/driftLight/uniforms.js +5 -6
  85. package/dist/render/driftLight/worldLightField.d.ts +41 -0
  86. package/dist/render/driftLight/worldLightField.js +66 -0
  87. package/dist/render/fixtureAtlas.d.ts +20 -0
  88. package/dist/render/fixtureAtlas.js +49 -0
  89. package/dist/render/glass.d.ts +45 -0
  90. package/dist/render/glass.js +39 -0
  91. package/dist/render/glassCasters.d.ts +40 -0
  92. package/dist/render/glassCasters.js +84 -0
  93. package/dist/render/glassShadow.d.ts +78 -0
  94. package/dist/render/glassShadow.js +125 -0
  95. package/dist/render/globalMediumPass.d.ts +2 -3
  96. package/dist/render/globalMediumPass.js +2 -8
  97. package/dist/render/gpudriven/lit.d.ts +12 -0
  98. package/dist/render/gpudriven/lit.js +23 -0
  99. package/dist/render/gpudriven/materialTable.d.ts +15 -5
  100. package/dist/render/gpudriven/materialTable.js +26 -6
  101. package/dist/render/gpudriven/pipeline.d.ts +1 -1
  102. package/dist/render/gpudriven/pipeline.js +4 -3
  103. package/dist/render/gpudriven/sunGlass.d.ts +35 -0
  104. package/dist/render/gpudriven/sunGlass.js +89 -0
  105. package/dist/render/instanceCull.d.ts +63 -0
  106. package/dist/render/instanceCull.js +160 -0
  107. package/dist/render/instances.d.ts +15 -0
  108. package/dist/render/lightBudget.d.ts +36 -35
  109. package/dist/render/lightBudget.js +38 -37
  110. package/dist/render/lightGrid.d.ts +42 -0
  111. package/dist/render/lightGrid.js +87 -0
  112. package/dist/render/mesh.d.ts +18 -24
  113. package/dist/render/mesh.js +51 -68
  114. package/dist/render/occlusion.d.ts +10 -0
  115. package/dist/render/occlusion.js +36 -7
  116. package/dist/render/pointGlassTint.d.ts +59 -0
  117. package/dist/render/pointGlassTint.js +198 -0
  118. package/dist/render/pointLightSelection.d.ts +7 -0
  119. package/dist/render/pointLightSelection.js +29 -4
  120. package/dist/render/pointShadowArray.d.ts +56 -3
  121. package/dist/render/pointShadowArray.js +127 -16
  122. package/dist/render/pointShadowMap.d.ts +23 -1
  123. package/dist/render/pointShadowMap.js +27 -1
  124. package/dist/render/rainField.d.ts +13 -0
  125. package/dist/render/rainField.js +27 -5
  126. package/dist/render/recon/changeFrames.d.ts +15 -0
  127. package/dist/render/recon/changeFrames.js +21 -0
  128. package/dist/render/recon/lateDepth.d.ts +11 -0
  129. package/dist/render/recon/lateDepth.js +70 -0
  130. package/dist/render/recon/lateRouting.d.ts +14 -0
  131. package/dist/render/recon/lateRouting.js +18 -0
  132. package/dist/render/recon/mover.d.ts +57 -0
  133. package/dist/render/recon/mover.js +86 -0
  134. package/dist/render/recon/resolve.d.ts +34 -4
  135. package/dist/render/recon/resolve.js +161 -20
  136. package/dist/render/renderQuality.d.ts +35 -0
  137. package/dist/render/renderQuality.js +7 -0
  138. package/dist/render/rendererHarness.js +16 -0
  139. package/dist/render/sceneTarget.d.ts +9 -1
  140. package/dist/render/sceneTarget.js +60 -12
  141. package/dist/render/shaders/depth.d.ts +16 -1
  142. package/dist/render/shaders/depth.js +94 -15
  143. package/dist/render/shaders/flat/directionalShadow.js +91 -6
  144. package/dist/render/shaders/flat/driftLight.d.ts +1 -1
  145. package/dist/render/shaders/flat/driftLight.js +32 -7
  146. package/dist/render/shaders/flat/glassTint.d.ts +6 -0
  147. package/dist/render/shaders/flat/glassTint.js +25 -0
  148. package/dist/render/shaders/flat/index.d.ts +13 -0
  149. package/dist/render/shaders/flat/index.js +9 -3
  150. package/dist/render/shaders/flat/lampGlass.d.ts +1 -0
  151. package/dist/render/shaders/flat/lampGlass.js +183 -0
  152. package/dist/render/shaders/flat/main.d.ts +1 -1
  153. package/dist/render/shaders/flat/main.js +190 -38
  154. package/dist/render/shaders/flat/pointShadow.d.ts +1 -1
  155. package/dist/render/shaders/flat/pointShadow.js +2 -0
  156. package/dist/render/shaders/flat/preamble.js +96 -26
  157. package/dist/render/shaders/flat/surfaceEffects.d.ts +29 -0
  158. package/dist/render/shaders/flat/surfaceEffects.js +152 -0
  159. package/dist/render/shaders/generated/depth.wgsl.d.ts +230 -7
  160. package/dist/render/shaders/generated/depth.wgsl.js +230 -7
  161. package/dist/render/shaders/generated/flat.wgsl.d.ts +1644 -1596
  162. package/dist/render/shaders/generated/flat.wgsl.js +1683 -1621
  163. package/dist/render/shaders/generated/globalMedium.wgsl.d.ts +3 -13
  164. package/dist/render/shaders/generated/globalMedium.wgsl.js +3 -13
  165. package/dist/render/shaders/generated/lightVolume.wgsl.d.ts +3 -13
  166. package/dist/render/shaders/generated/lightVolume.wgsl.js +21 -17
  167. package/dist/render/shaders/generated/octahedralResolve.wgsl.d.ts +29 -0
  168. package/dist/render/shaders/generated/octahedralResolve.wgsl.js +29 -0
  169. package/dist/render/shaders/generated/rush.wgsl.d.ts +21 -16
  170. package/dist/render/shaders/generated/rush.wgsl.js +21 -16
  171. package/dist/render/shaders/generated/sdfText.wgsl.d.ts +2 -2
  172. package/dist/render/shaders/generated/sdfText.wgsl.js +2 -2
  173. package/dist/render/shaders/generated/text.wgsl.d.ts +1 -1
  174. package/dist/render/shaders/generated/text.wgsl.js +1 -1
  175. package/dist/render/shaders/gi/driftLight.wgsl.d.ts +1 -1
  176. package/dist/render/shaders/gi/driftLight.wgsl.js +27 -4
  177. package/dist/render/shaders/globalMedium.js +6 -6
  178. package/dist/render/shaders/gpudriven/blendRaster.wgsl.d.ts +7 -0
  179. package/dist/render/shaders/gpudriven/blendRaster.wgsl.js +86 -1
  180. package/dist/render/shaders/gpudriven/cull.wgsl.d.ts +1 -1
  181. package/dist/render/shaders/gpudriven/cull.wgsl.js +11 -2
  182. package/dist/render/shaders/gpudriven/materialTable.wgsl.d.ts +1 -1
  183. package/dist/render/shaders/gpudriven/materialTable.wgsl.js +3 -1
  184. package/dist/render/shaders/gpudriven/shade.wgsl.js +50 -1
  185. package/dist/render/shaders/gpudriven/sunGlass.wgsl.d.ts +9 -0
  186. package/dist/render/shaders/gpudriven/sunGlass.wgsl.js +133 -0
  187. package/dist/render/shaders/lightVolume.js +6 -6
  188. package/dist/render/shaders/octahedralResolve.d.ts +8 -0
  189. package/dist/render/shaders/octahedralResolve.js +48 -17
  190. package/dist/render/shaders/recon/lateDepth.wgsl.d.ts +1 -0
  191. package/dist/render/shaders/recon/lateDepth.wgsl.js +69 -0
  192. package/dist/render/shaders/recon/motion.wgsl.d.ts +1 -1
  193. package/dist/render/shaders/recon/motion.wgsl.js +84 -0
  194. package/dist/render/shaders/recon/resolve.wgsl.d.ts +8 -7
  195. package/dist/render/shaders/recon/resolve.wgsl.js +161 -27
  196. package/dist/render/shaders/rush.js +11 -1
  197. package/dist/render/shaders/sdfText.d.ts +1 -1
  198. package/dist/render/shaders/sdfText.js +3 -2
  199. package/dist/render/shaders/text.d.ts +1 -1
  200. package/dist/render/shaders/text.js +18 -9
  201. package/dist/render/shadowCasters.d.ts +20 -1
  202. package/dist/render/shadowCasters.js +15 -1
  203. package/dist/render/shadowMap.d.ts +54 -5
  204. package/dist/render/shadowMap.js +190 -36
  205. package/dist/render/sunGlassTint.d.ts +25 -0
  206. package/dist/render/sunGlassTint.js +79 -0
  207. package/dist/render/surfaceEffects.d.ts +83 -0
  208. package/dist/render/surfaceEffects.js +75 -0
  209. package/dist/render/surfaceScene.d.ts +8 -0
  210. package/dist/render/surfaceScene.js +8 -0
  211. package/dist/render/surfaceTexture.d.ts +18 -2
  212. package/dist/render/surfaceTexture.js +68 -15
  213. package/dist/render/temporalAa.d.ts +25 -6
  214. package/dist/render/temporalAa.js +45 -15
  215. package/dist/render/textureSource.d.ts +30 -0
  216. package/dist/render/textureSource.js +53 -0
  217. package/dist/render/translucentQueue.d.ts +6 -0
  218. package/dist/render/translucentQueue.js +15 -1
  219. package/dist/render/vertexDefaults.js +2 -0
  220. package/dist/world/hlod.d.ts +104 -0
  221. package/dist/world/hlod.js +214 -0
  222. package/package.json +6 -6
  223. package/src/geometry/boxCsg.ts +186 -0
  224. package/src/geometry/csg.ts +384 -0
  225. package/src/geometry/meshBuilder.ts +15 -0
  226. package/src/geometry/meshConcat.ts +1 -0
  227. package/src/geometry/pixelFont.ts +2 -0
  228. package/src/geometry/solid.ts +189 -0
  229. package/src/geometry/solidFlat.ts +522 -0
  230. package/src/geometry/solidHarness.ts +83 -0
  231. package/src/geometry/solidNormals.ts +103 -0
  232. package/src/geometry/solidRound.ts +443 -0
  233. package/src/geometry/solidWriter.ts +58 -0
  234. package/src/index.ts +61 -1
  235. package/src/render/backend/api.ts +36 -11
  236. package/src/render/backend/drawFog.ts +31 -0
  237. package/src/render/backend/materialChanges.ts +9 -2
  238. package/src/render/backend/webgl2/glassTintPrograms.ts +58 -0
  239. package/src/render/backend/webgl2/instanced.ts +80 -14
  240. package/src/render/backend/webgl2/renderer.ts +700 -250
  241. package/src/render/backend/webgpu/buffers.ts +92 -5
  242. package/src/render/backend/webgpu/depthPass.ts +7 -2
  243. package/src/render/backend/webgpu/drawCommand.ts +6 -0
  244. package/src/render/backend/webgpu/flatPass.ts +62 -11
  245. package/src/render/backend/webgpu/glassTintPass.ts +245 -0
  246. package/src/render/backend/webgpu/gpuDrivenPass.ts +349 -5
  247. package/src/render/backend/webgpu/instanceCullPass.ts +165 -0
  248. package/src/render/backend/webgpu/instanced.ts +42 -4
  249. package/src/render/backend/webgpu/latePass.ts +161 -0
  250. package/src/render/backend/webgpu/lightVolumePass.ts +5 -1
  251. package/src/render/backend/webgpu/motionPass.ts +535 -0
  252. package/src/render/backend/webgpu/pipelineCache.ts +12 -1
  253. package/src/render/backend/webgpu/pointGlassTintPass.ts +233 -0
  254. package/src/render/backend/webgpu/pointShadowPass.ts +158 -26
  255. package/src/render/backend/webgpu/renderer.ts +1671 -670
  256. package/src/render/backend/webgpu/scatterPass.ts +2 -1
  257. package/src/render/backend/webgpu/sdfTextPass.ts +6 -1
  258. package/src/render/backend/webgpu/shaders/instanceCull.wgsl.ts +61 -0
  259. package/src/render/backend/webgpu/skinPaletteRing.ts +4 -0
  260. package/src/render/backend/webgpu/snapshotMips.ts +80 -0
  261. package/src/render/backend/webgpu/surfaceTexturePass.ts +91 -51
  262. package/src/render/driftLight/bake.ts +85 -62
  263. package/src/render/driftLight/denseField.ts +149 -0
  264. package/src/render/driftLight/lightField.ts +33 -18
  265. package/src/render/driftLight/presence.ts +69 -0
  266. package/src/render/driftLight/uniforms.ts +8 -9
  267. package/src/render/driftLight/worldLightField.ts +83 -0
  268. package/src/render/fixtureAtlas.ts +63 -0
  269. package/src/render/glass.ts +74 -0
  270. package/src/render/glassCasters.ts +164 -0
  271. package/src/render/glassShadow.ts +153 -0
  272. package/src/render/globalMediumPass.ts +4 -11
  273. package/src/render/gpudriven/lit.ts +31 -0
  274. package/src/render/gpudriven/materialTable.ts +28 -6
  275. package/src/render/gpudriven/pipeline.ts +4 -3
  276. package/src/render/gpudriven/sunGlass.ts +117 -0
  277. package/src/render/instanceCull.ts +205 -0
  278. package/src/render/instances.ts +16 -0
  279. package/src/render/lightBudget.ts +41 -38
  280. package/src/render/lightGrid.ts +130 -0
  281. package/src/render/mesh.ts +59 -72
  282. package/src/render/occlusion.ts +51 -7
  283. package/src/render/pointGlassTint.ts +250 -0
  284. package/src/render/pointLightSelection.ts +72 -4
  285. package/src/render/pointShadowArray.ts +188 -22
  286. package/src/render/pointShadowMap.ts +41 -0
  287. package/src/render/rainField.ts +39 -5
  288. package/src/render/recon/changeFrames.ts +24 -0
  289. package/src/render/recon/lateDepth.ts +83 -0
  290. package/src/render/recon/lateRouting.ts +29 -0
  291. package/src/render/recon/mover.ts +118 -0
  292. package/src/render/recon/resolve.ts +206 -20
  293. package/src/render/renderQuality.ts +47 -0
  294. package/src/render/rendererHarness.ts +16 -0
  295. package/src/render/sceneTarget.ts +62 -11
  296. package/src/render/shaders/depth.ts +118 -15
  297. package/src/render/shaders/flat/directionalShadow.ts +97 -6
  298. package/src/render/shaders/flat/driftLight.ts +32 -7
  299. package/src/render/shaders/flat/glassTint.ts +25 -0
  300. package/src/render/shaders/flat/index.ts +22 -3
  301. package/src/render/shaders/flat/lampGlass.ts +185 -0
  302. package/src/render/shaders/flat/main.ts +190 -38
  303. package/src/render/shaders/flat/pointShadow.ts +2 -0
  304. package/src/render/shaders/flat/preamble.ts +96 -26
  305. package/src/render/shaders/flat/surfaceEffects.ts +152 -0
  306. package/src/render/shaders/generated/depth.wgsl.ts +237 -7
  307. package/src/render/shaders/generated/flat.wgsl.ts +1685 -1621
  308. package/src/render/shaders/generated/globalMedium.wgsl.ts +3 -13
  309. package/src/render/shaders/generated/lightVolume.wgsl.ts +23 -17
  310. package/src/render/shaders/generated/octahedralResolve.wgsl.ts +30 -0
  311. package/src/render/shaders/generated/rush.wgsl.ts +21 -16
  312. package/src/render/shaders/generated/sdfText.wgsl.ts +2 -2
  313. package/src/render/shaders/generated/text.wgsl.ts +1 -1
  314. package/src/render/shaders/gi/driftLight.wgsl.ts +27 -4
  315. package/src/render/shaders/globalMedium.ts +6 -6
  316. package/src/render/shaders/gpudriven/blendRaster.wgsl.ts +88 -1
  317. package/src/render/shaders/gpudriven/cull.wgsl.ts +11 -2
  318. package/src/render/shaders/gpudriven/materialTable.wgsl.ts +3 -1
  319. package/src/render/shaders/gpudriven/shade.wgsl.ts +50 -1
  320. package/src/render/shaders/gpudriven/sunGlass.wgsl.ts +139 -0
  321. package/src/render/shaders/lightVolume.ts +6 -6
  322. package/src/render/shaders/octahedralResolve.ts +50 -17
  323. package/src/render/shaders/recon/lateDepth.wgsl.ts +72 -0
  324. package/src/render/shaders/recon/motion.wgsl.ts +84 -0
  325. package/src/render/shaders/recon/resolve.wgsl.ts +161 -27
  326. package/src/render/shaders/rush.ts +11 -1
  327. package/src/render/shaders/sdfText.ts +3 -2
  328. package/src/render/shaders/text.ts +18 -9
  329. package/src/render/shadowCasters.ts +28 -1
  330. package/src/render/shadowMap.ts +231 -37
  331. package/src/render/sunGlassTint.ts +86 -0
  332. package/src/render/surfaceEffects.ts +132 -0
  333. package/src/render/surfaceScene.ts +16 -0
  334. package/src/render/surfaceTexture.ts +110 -19
  335. package/src/render/temporalAa.ts +78 -14
  336. package/src/render/textureSource.ts +72 -0
  337. package/src/render/translucentQueue.ts +20 -1
  338. package/src/render/vertexDefaults.ts +2 -0
  339. package/src/world/hlod.ts +290 -0
@@ -26,6 +26,16 @@ export interface VertexAttribute {
26
26
  */
27
27
  export declare const VERTEX_LAYOUT: readonly VertexAttribute[];
28
28
  /** Geometry on the device, with the buffers bound in layout order. */
29
+ /**
30
+ * The texture coordinates of a mesh that names texture-array layers: (u, v, layer), three floats.
31
+ *
32
+ * **Three only where layers exist.** Every location is spent, so the layer rides in this attribute,
33
+ * and widening it for every textured mesh would add four bytes to each of its vertices for nothing.
34
+ * A mesh without layers keeps two, and the shader's `vec3` input reads z as 0 — WebGPU fills a
35
+ * component the format does not carry with 0 (and w with 1) — which is layer 0. The pipeline key
36
+ * carries `:layers`, so the two vertex layouts never share a pipeline.
37
+ */
38
+ export declare const LAYERED_UVS: VertexAttribute;
29
39
  export interface GpuMesh {
30
40
  /**
31
41
  * The buffers a draw binds, in the order `vertexBufferLayouts` describes them.
@@ -48,6 +58,15 @@ export interface GpuMesh {
48
58
  * its own does.
49
59
  */
50
60
  readonly vertexStride: number;
61
+ /**
62
+ * Byte offsets of the joint indices and weights in the interleaved vertex, or null for a mesh
63
+ * that supplied neither. For a pass that skins through a shader of its own — the motion pass —
64
+ * which has to find them without the `present` map the generated pipelines are keyed by.
65
+ */
66
+ readonly skinOffsets: {
67
+ readonly joints: number;
68
+ readonly weights: number;
69
+ } | null;
51
70
  readonly indexBuffer: GPUBuffer;
52
71
  readonly indexCount: number;
53
72
  /**
@@ -114,7 +133,16 @@ export interface GpuMesh {
114
133
  * one CPU-side copy of the vertex data per dynamic mesh, which is why the flag exists rather
115
134
  * than every mesh paying it.
116
135
  */
117
- readonly update: ((device: GPUDevice, positions: Float32Array, normals?: Float32Array) => void) | null;
136
+ readonly update: ((device: GPUDevice, positions: Float32Array, normals?: Float32Array, previous?: GPUBuffer | null) => void) | null;
137
+ /**
138
+ * A dynamic mesh's motion state, which the renderer owns: a buffer for last frame's interleaved
139
+ * rows, made the first time a reconstructing renderer rewrites the mesh, and the frame of the
140
+ * last rewrite. Null on a mesh created without `{ dynamic: true }`, which never moves a vertex.
141
+ */
142
+ readonly motion: {
143
+ previous: GPUBuffer | null;
144
+ changed: number;
145
+ } | null;
118
146
  dispose(): void;
119
147
  }
120
148
  /** A mesh on its way to the device, and the iterator that gets it there. */
@@ -84,6 +84,27 @@ export const VERTEX_LAYOUT = [
84
84
  */
85
85
  { name: 'channel', shaderLocation: 13, format: 'float32x4', components: 4, optional: true },
86
86
  ];
87
+ /** Geometry on the device, with the buffers bound in layout order. */
88
+ /**
89
+ * The texture coordinates of a mesh that names texture-array layers: (u, v, layer), three floats.
90
+ *
91
+ * **Three only where layers exist.** Every location is spent, so the layer rides in this attribute,
92
+ * and widening it for every textured mesh would add four bytes to each of its vertices for nothing.
93
+ * A mesh without layers keeps two, and the shader's `vec3` input reads z as 0 — WebGPU fills a
94
+ * component the format does not carry with 0 (and w with 1) — which is layer 0. The pipeline key
95
+ * carries `:layers`, so the two vertex layouts never share a pipeline.
96
+ */
97
+ export const LAYERED_UVS = {
98
+ name: 'uvs',
99
+ shaderLocation: 5,
100
+ format: 'float32x3',
101
+ components: 3,
102
+ optional: true,
103
+ };
104
+ /** The attribute as this mesh lays it out: the UV field widens where the mesh names layers. */
105
+ function laidOut(attribute, layered) {
106
+ return layered && attribute.name === 'uvs' ? LAYERED_UVS : attribute;
107
+ }
87
108
  /**
88
109
  * Upload a mesh, all of it, now.
89
110
  *
@@ -130,7 +151,8 @@ export function createGpuMeshIncremental(device, data, dynamic = false) {
130
151
  validateMeshData(data);
131
152
  const bounds = boundsOfPositions(data.positions, createBounds());
132
153
  const vertexCount = data.positions.length / 3;
133
- const supplied = VERTEX_LAYOUT.filter((attribute) => !attribute.optional || data[attribute.name] !== undefined);
154
+ const layered = data.layers !== undefined;
155
+ const supplied = VERTEX_LAYOUT.filter((attribute) => !attribute.optional || data[attribute.name] !== undefined).map((attribute) => laidOut(attribute, layered));
134
156
  /* Interleaved: one stride holding every attribute the mesh actually supplied. */
135
157
  const stride = supplied.reduce((sum, attribute) => sum + attribute.components * 4, 0);
136
158
  const step = stride / 4;
@@ -235,6 +257,7 @@ export function createGpuMeshIncremental(device, data, dynamic = false) {
235
257
  progress,
236
258
  hasTangents: data.tangents !== undefined,
237
259
  isSkinned: data.joints !== undefined,
260
+ skinOffsets: skinOffsetsOf(supplied),
238
261
  hasChannel: data.channel !== undefined,
239
262
  morph: data.morphTargets === undefined || data.morphTargetCount === undefined
240
263
  ? null
@@ -270,7 +293,14 @@ function* uploadSteps(job) {
270
293
  let fieldOffset = 0;
271
294
  for (const attribute of supplied) {
272
295
  const source = data[attribute.name];
273
- interleaveField(interleaved, source, attribute.components, step, fieldOffset, from, to);
296
+ if (attribute === LAYERED_UVS) {
297
+ /* (u, v) from the coordinates and the layer after them, one field of three. */
298
+ interleaveField(interleaved, source, 2, step, fieldOffset, from, to);
299
+ interleaveField(interleaved, data.layers, 1, step, fieldOffset + 2, from, to);
300
+ }
301
+ else {
302
+ interleaveField(interleaved, source, attribute.components, step, fieldOffset, from, to);
303
+ }
274
304
  fieldOffset += attribute.components;
275
305
  }
276
306
  device.queue.writeBuffer(vertices, from * stride, interleaved, from * step, (to - from) * step);
@@ -329,12 +359,31 @@ function interleaveField(rows, source, width, step, fieldOffset, from, to) {
329
359
  rows[write] = source[read];
330
360
  }
331
361
  }
362
+ /**
363
+ * Where the joints and weights sit in a vertex of `supplied`, the attributes a mesh interleaves in
364
+ * layout order; null unless it supplied both.
365
+ */
366
+ function skinOffsetsOf(supplied) {
367
+ let floats = 0;
368
+ let joints = -1;
369
+ let weights = -1;
370
+ for (const attribute of supplied) {
371
+ if (attribute.name === 'joints')
372
+ joints = floats * 4;
373
+ if (attribute.name === 'weights')
374
+ weights = floats * 4;
375
+ floats += attribute.components;
376
+ }
377
+ return joints >= 0 && weights >= 0 ? { joints, weights } : null;
378
+ }
332
379
  /** The handle, made where it can see only what it hands out. */
333
380
  function gpuMeshOf(parts) {
381
+ const motion = parts.update === null ? null : { previous: null, changed: -1 };
334
382
  const { vertices, constants, indexBuffer, progress } = parts;
335
383
  return {
336
384
  vertexBuffers: [vertices, constants],
337
385
  vertexStride: parts.stride,
386
+ skinOffsets: parts.skinOffsets,
338
387
  indexBuffer,
339
388
  indexCount: parts.indexCount,
340
389
  bounds: parts.bounds,
@@ -346,7 +395,9 @@ function gpuMeshOf(parts) {
346
395
  hasChannel: parts.hasChannel,
347
396
  morph: parts.morph,
348
397
  update: parts.update,
398
+ motion,
349
399
  dispose() {
400
+ motion?.previous?.destroy();
350
401
  vertices.destroy();
351
402
  constants.destroy();
352
403
  indexBuffer.destroy();
@@ -359,12 +410,16 @@ function gpuMeshOf(parts) {
359
410
  * receive have to still be there.
360
411
  */
361
412
  function dynamicUpdate(interleaved, vertices, bounds, vertexCount, step, positionField, normalField) {
362
- return (target, positions, normals) => {
413
+ return (target, positions, normals, previous = null) => {
363
414
  if (positions.length !== vertexCount * 3) {
364
415
  throw new Error(`this mesh has ${vertexCount} vertices and the update has ${positions.length / 3}. ` +
365
416
  "A mesh's vertex count is fixed at creation: the index buffer, the pipeline and " +
366
417
  'every other attribute are sized against it.');
367
418
  }
419
+ /* The rows as they stand are last frame's, handed over before a position of them changes.
420
+ `writeBuffer` copies at the call, so patching the array afterwards cannot reach this copy. */
421
+ if (previous !== null)
422
+ target.queue.writeBuffer(previous, 0, interleaved);
368
423
  /* Everything that decides whether this is on screen starts from the bounds, so a cloth
369
424
  that blew sideways out of its original box would be culled while still visible. */
370
425
  boundsOfPositions(positions, bounds);
@@ -423,7 +478,9 @@ instanced = false) {
423
478
  const interleaved = [];
424
479
  const constants = [];
425
480
  let offset = 0;
426
- VERTEX_LAYOUT.forEach((attribute, index) => {
481
+ const layered = present['layers'] === true;
482
+ VERTEX_LAYOUT.forEach((declared, index) => {
483
+ const attribute = laidOut(declared, layered);
427
484
  /*
428
485
  * **The instanced pipeline reclaims the two skinning locations, and it is allowed to because
429
486
  * it cannot skin.** Locations 11 and 12 are the joint indices and weights; the INSTANCED
@@ -117,7 +117,7 @@ export declare const DEPTH_FRAG_BINDING: 1;
117
117
  export declare const DEPTH_PREVIOUS_BINDING: {
118
118
  readonly texture: 32;
119
119
  readonly sampler: 33;
120
- readonly type: "sampler2D";
120
+ readonly type: "sampler2DArray";
121
121
  };
122
122
  /** Where the skinned vertex stage reads the joint palette. Generated, not chosen here. */
123
123
  export declare const DEPTH_PALETTE_BINDING: {
@@ -104,7 +104,8 @@ cutout = false) {
104
104
  {
105
105
  binding: PREVIOUS.texture,
106
106
  visibility: VISIBILITY_FRAGMENT,
107
- texture: { sampleType: 'unfilterable-float' },
107
+ /* An array since the sun's maps became one (`shadowMap.ts`): the peel reads its static layer. */
108
+ texture: { sampleType: 'unfilterable-float', viewDimension: '2d-array' },
108
109
  },
109
110
  {
110
111
  binding: PREVIOUS.sampler,
@@ -118,7 +119,11 @@ cutout = false) {
118
119
  {
119
120
  binding: CUTOUT_MAP.texture,
120
121
  visibility: VISIBILITY_FRAGMENT,
121
- texture: { sampleType: 'float' },
122
+ /* A surface texture, and every surface texture is a `2d-array` view. */
123
+ texture: {
124
+ sampleType: 'float',
125
+ viewDimension: '2d-array',
126
+ },
122
127
  },
123
128
  {
124
129
  binding: CUTOUT_MAP.sampler,
@@ -24,6 +24,11 @@ export interface DrawCommand {
24
24
  indexed: boolean;
25
25
  count: number;
26
26
  instances: number;
27
+ /**
28
+ * A `drawIndexedIndirect` record to draw from instead of `count` and `instances`: an instance
29
+ * cull's survivors, counted on the device. Null for every other draw.
30
+ */
31
+ indirect: GPUBuffer | null;
27
32
  }
28
33
  export interface CommandPool {
29
34
  commands: DrawCommand[];
@@ -25,6 +25,7 @@ function emptyCommand() {
25
25
  indexed: false,
26
26
  count: 0,
27
27
  instances: 1,
28
+ indirect: null,
28
29
  };
29
30
  }
30
31
  export function createCommandPool(capacity) {
@@ -58,6 +58,8 @@ export interface StageBindings {
58
58
  readonly stride?: number;
59
59
  }>>;
60
60
  readonly textures: Readonly<Record<string, TextureBinding>>;
61
+ /** Each pipeline-overridable constant's id, by the GLSL name it was declared under. */
62
+ readonly overrides?: Readonly<Record<string, number>>;
61
63
  }
62
64
  /** The key for a set of vertex flags: the same rule `variantKey` uses, flags on, sorted, joined. */
63
65
  export declare function flatVertexKey(skinned: boolean, morphed: boolean, instanced?: boolean): string;
@@ -176,8 +178,11 @@ export declare function flatPipeline(cache: PipelineCache, device: GPUDevice, la
176
178
  * `depthMask` alone and only turns blending on. A translucent part of a loaded model is a
177
179
  * pane of glass in a solid object, not a particle, and it has to occlude what is behind it
178
180
  * for the rest of the model to sort correctly.
181
+ *
182
+ * `'additive'` adds instead: colour times alpha onto the target, its alpha kept. The key must
183
+ * carry it (`|add`).
179
184
  */
180
- translucent?: boolean,
185
+ translucent?: boolean | 'additive',
181
186
  /** Whether this pipeline reads a joint palette. See `FlatVertexOptions`. */
182
187
  skinned?: boolean,
183
188
  /** Whether this pipeline reads morph deltas. See `FlatVertexOptions`. */
@@ -2,6 +2,13 @@ import { DEPTH_COMPARE, DEPTH_FORMAT, depthOffsetForLayer } from '../../depthCon
2
2
  import { FLAT_BINDINGS, FLAT_FRAG_WGSL, FLAT_VERT_WGSL, } from '../../shaders/generated/flat.wgsl.js';
3
3
  import { vertexBufferLayouts } from './buffers.js';
4
4
  import { shaderModule } from './shaderModules.js';
5
+ /** The material maps the flat pass reads: `2d-array` colour, filtered, not shadow maps. */
6
+ const SURFACE_MAPS = new Set([
7
+ 'uAlbedo',
8
+ 'uNormalMap',
9
+ 'uOrmMap',
10
+ 'uEmissiveMap',
11
+ ]);
5
12
  /**
6
13
  * The key for a set of flags.
7
14
  *
@@ -107,6 +114,20 @@ export function flatFragmentBindings(variant) {
107
114
  throw new Error(`flatPass: no bindings for variant "${variant}"`);
108
115
  return found;
109
116
  }
117
+ /**
118
+ * The lit fragment stage's constants, set from the cache: the glass switch, keyed by its **id**.
119
+ *
120
+ * Not by its name, because an override declared with `@id` answers to the id alone — WebGPU's
121
+ * "pipeline-overridable constant identifier string" — and naga writes one on every specialisation
122
+ * constant. Keyed by name, Chrome refused every lit pipeline: *Pipeline overridable constant
123
+ * "GLASS_SHADOWS" not found*, and the frame drew nothing.
124
+ */
125
+ function flatFragmentConstants(variant, cache) {
126
+ const id = flatFragmentBindings(variant).overrides?.['GLASS_SHADOWS'];
127
+ if (id === undefined)
128
+ throw new Error(`flatPass: variant "${variant}" has no glass switch`);
129
+ return { [String(id)]: cache.glassShadows ? 1 : 0 };
130
+ }
110
131
  /** The depth format both backends compare with. */
111
132
  /* Re-exported so the twenty passes that already import it from here do not each learn a new
112
133
  module; `depthConvention.ts` is where the decision lives and why. */
@@ -182,7 +203,21 @@ export function createFlatBindGroupLayout(device, variant, skinned = false, morp
182
203
  const integer = texture.type === 'usampler2D' || texture.type === 'usampler3D';
183
204
  /* DriftLight's brick index and atlas, the lit pass's only volumes. See `driftLight.ts`. */
184
205
  const volume = texture.type === 'sampler3D' || texture.type === 'usampler3D';
185
- const isShadow = integer || (name !== 'uEnvironment' && (name.endsWith('ShadowMap') || cube || array));
206
+ /* Glass tints are colour, mipmapped and filtered: a frosted pane is read at a coarser level. */
207
+ const tint = name.endsWith('GlassTints');
208
+ /*
209
+ * **The four material maps are arrays too, and they are colour.** Every surface texture is a
210
+ * `2d-array` view since texture arrays landed, so the rule below — an array is a shadow map —
211
+ * would declare albedo unfilterable and bind a non-filtering sampler to it, which reads as
212
+ * every textured surface going nearest-neighbour and losing its mip chain. Named, as
213
+ * `uEnvironment` is, because a pattern wide enough to catch them catches the shadow arrays.
214
+ */
215
+ const surface = SURFACE_MAPS.has(name);
216
+ const isShadow = integer ||
217
+ (!tint &&
218
+ !surface &&
219
+ name !== 'uEnvironment' &&
220
+ (name.endsWith('ShadowMap') || cube || array));
186
221
  entries.push({
187
222
  binding: texture.texture,
188
223
  visibility: VISIBILITY_FRAGMENT,
@@ -332,6 +367,9 @@ export function flatPipeline(cache, device, layout, variant, key, present,
332
367
  * `depthMask` alone and only turns blending on. A translucent part of a loaded model is a
333
368
  * pane of glass in a solid object, not a particle, and it has to occlude what is behind it
334
369
  * for the rest of the model to sort correctly.
370
+ *
371
+ * `'additive'` adds instead: colour times alpha onto the target, its alpha kept. The key must
372
+ * carry it (`|add`).
335
373
  */
336
374
  translucent = false,
337
375
  /** Whether this pipeline reads a joint palette. See `FlatVertexOptions`. */
@@ -449,21 +487,31 @@ doubleSided = false) {
449
487
  code: FLAT_FRAG_WGSL[variant] ?? '',
450
488
  }),
451
489
  entryPoint: 'main',
490
+ /* Every variant declares it, so every lit pipeline may set it: see `PipelineCache`. */
491
+ constants: flatFragmentConstants(variant, cache),
452
492
  targets: [
453
493
  oitTarget(oit) ??
454
- (translucent
494
+ (translucent === 'additive'
455
495
  ? {
456
496
  format: cache.format,
457
497
  blend: {
458
- color: {
459
- srcFactor: 'src-alpha',
460
- dstFactor: 'one-minus-src-alpha',
461
- operation: 'add',
462
- },
463
- alpha: { srcFactor: 'one', dstFactor: 'one-minus-src-alpha', operation: 'add' },
498
+ color: { srcFactor: 'src-alpha', dstFactor: 'one', operation: 'add' },
499
+ alpha: { srcFactor: 'zero', dstFactor: 'one', operation: 'add' },
464
500
  },
465
501
  }
466
- : { format: cache.format }),
502
+ : translucent
503
+ ? {
504
+ format: cache.format,
505
+ blend: {
506
+ color: {
507
+ srcFactor: 'src-alpha',
508
+ dstFactor: 'one-minus-src-alpha',
509
+ operation: 'add',
510
+ },
511
+ alpha: { srcFactor: 'one', dstFactor: 'one-minus-src-alpha', operation: 'add' },
512
+ },
513
+ }
514
+ : { format: cache.format }),
467
515
  ],
468
516
  },
469
517
  primitive: { topology: 'triangle-list', cullMode: doubleSided ? 'none' : 'back' },
@@ -0,0 +1,28 @@
1
+ import type { ResolvedGlass } from '../../glass.ts';
2
+ import type { PipelineCache } from './pipelineCache.ts';
3
+ import { UniformRing } from './uniformRing.ts';
4
+ export type GlassTintKind = 'rigid' | 'instanced' | 'skinned' | 'cutout' | 'instancedCutout';
5
+ /** The multiplying state: colour times what passes, clarity times clarity, order-free. */
6
+ export declare const GLASS_TINT_BLEND: GPUBlendState;
7
+ export declare class GpuGlassTint {
8
+ readonly frag: UniformRing;
9
+ private readonly layouts;
10
+ private plainGroup;
11
+ private groupVertBuffer;
12
+ private groupFragBuffer;
13
+ private readonly cutoutGroups;
14
+ private readonly skinnedGroups;
15
+ private readonly device;
16
+ constructor(device: GPUDevice);
17
+ /** One pane's block: what it lets through, how clear it is, and the light it is drawn for. */
18
+ writePane(slot: number, glass: ResolvedGlass, light: Float32Array): void;
19
+ /** The plain group, rebuilt only when a ring has grown a new buffer under it. */
20
+ group(vertBuffer: GPUBuffer): GPUBindGroup;
21
+ /** A cutout pane's group, one per map, built the first time that map casts colour. */
22
+ cutoutGroup(vertBuffer: GPUBuffer, view: GPUTextureView, sampler: GPUSampler): GPUBindGroup;
23
+ /** A skinned pane's group, one per palette view. */
24
+ skinnedGroup(vertBuffer: GPUBuffer, palette: GPUTextureView): GPUBindGroup;
25
+ /** The pipeline for one kind of caster, cull mode and vertex layout, cached by key. */
26
+ pipeline(cache: PipelineCache, kind: GlassTintKind, key: string, present: Readonly<Record<string, boolean>>, cullMode: GPUCullMode): GPURenderPipeline;
27
+ dispose(): void;
28
+ }
@@ -0,0 +1,206 @@
1
+ /**
2
+ * What a pane lets through, drawn into a shadow's tint layer on this device: the layouts, the
3
+ * per-draw block, the bind groups and the pipelines for the five tint programs (`depth.ts`).
4
+ *
5
+ * **The vertex half is the depth pass's**, block for block — the tint's vertex stages are the depth
6
+ * ones with the world position handed on — so a draw's vertex slot comes from the shadow pass's own
7
+ * ring and only the pane's 32 bytes are this file's. **The fragment half multiplies**: an
8
+ * `rgba8unorm` target cleared to white, `dst × src` for colour and `dst-alpha × src` for clarity, and
9
+ * no depth, so every pane on a ray counts in any order. Built the first time glass casts, so a scene
10
+ * with no glass builds none of it.
11
+ */
12
+ import { DEPTH_BINDINGS } from '../../shaders/generated/depth.wgsl.js';
13
+ import { GLASS_TINT_CUTOUT_FRAG_WGSL, GLASS_TINT_CUTOUT_VERT_WGSL, GLASS_TINT_FRAG_WGSL, GLASS_TINT_INSTANCED_CUTOUT_VERT_WGSL, GLASS_TINT_INSTANCED_VERT_WGSL, GLASS_TINT_SKINNED_VERT_WGSL, GLASS_TINT_VERT_WGSL, } from '../../shaders/generated/depth.wgsl.js';
14
+ import { vertexBufferLayouts } from './buffers.js';
15
+ import { shaderModule } from './shaderModules.js';
16
+ import { UniformRing } from './uniformRing.js';
17
+ const VISIBILITY_VERTEX = 0x1;
18
+ const VISIBILITY_FRAGMENT = 0x2;
19
+ const USAGE_UNIFORM_DST = 0x40 | 0x8; // UNIFORM | COPY_DST
20
+ const VERT = DEPTH_BINDINGS.GLASS_TINT_VERT;
21
+ const CUTOUT_VERT = DEPTH_BINDINGS.GLASS_TINT_CUTOUT_VERT;
22
+ const FRAG = DEPTH_BINDINGS.GLASS_TINT_FRAG;
23
+ const CUTOUT_MAP = DEPTH_BINDINGS.GLASS_TINT_CUTOUT_FRAG.textures.uCutoutMap;
24
+ const PALETTE = DEPTH_BINDINGS.GLASS_TINT_SKINNED_VERT.textures.uJointPalette;
25
+ const PANE = FRAG.fields.uGlassPane.offset;
26
+ const LIGHT = FRAG.fields.uGlassLight.offset;
27
+ /** Panes a frame can tint: a pane is a draw per shadow it reaches, and panes are few. */
28
+ const MAX_TINT_DRAWS = 512;
29
+ /** The multiplying state: colour times what passes, clarity times clarity, order-free. */
30
+ export const GLASS_TINT_BLEND = {
31
+ color: { operation: 'add', srcFactor: 'dst', dstFactor: 'zero' },
32
+ alpha: { operation: 'add', srcFactor: 'dst-alpha', dstFactor: 'zero' },
33
+ };
34
+ export class GpuGlassTint {
35
+ frag;
36
+ layouts;
37
+ plainGroup = null;
38
+ groupVertBuffer = null;
39
+ groupFragBuffer = null;
40
+ cutoutGroups = new Map();
41
+ skinnedGroups = new Map();
42
+ device;
43
+ constructor(device) {
44
+ this.device = device;
45
+ this.frag = new UniformRing(device, FRAG.uniformSize, MAX_TINT_DRAWS, USAGE_UNIFORM_DST, 'glassTint.ring');
46
+ const vert = (size) => ({
47
+ binding: VERT.uniforms,
48
+ visibility: VISIBILITY_VERTEX,
49
+ buffer: { type: 'uniform', hasDynamicOffset: true, minBindingSize: size },
50
+ });
51
+ const frag = {
52
+ binding: FRAG.uniforms,
53
+ visibility: VISIBILITY_FRAGMENT,
54
+ buffer: { type: 'uniform', hasDynamicOffset: true, minBindingSize: FRAG.uniformSize },
55
+ };
56
+ this.layouts = {
57
+ plain: device.createBindGroupLayout({
58
+ label: 'glassTint.layout',
59
+ entries: [vert(VERT.uniformSize), frag],
60
+ }),
61
+ cutout: device.createBindGroupLayout({
62
+ label: 'glassTint.layout.cutout',
63
+ entries: [
64
+ vert(CUTOUT_VERT.uniformSize),
65
+ frag,
66
+ {
67
+ binding: CUTOUT_MAP.texture,
68
+ visibility: VISIBILITY_FRAGMENT,
69
+ /* A surface texture, and every surface texture is a `2d-array` view. */
70
+ texture: { sampleType: 'float', viewDimension: '2d-array' },
71
+ },
72
+ {
73
+ binding: CUTOUT_MAP.sampler,
74
+ visibility: VISIBILITY_FRAGMENT,
75
+ sampler: { type: 'filtering' },
76
+ },
77
+ ],
78
+ }),
79
+ skinned: device.createBindGroupLayout({
80
+ label: 'glassTint.layout.skinned',
81
+ entries: [
82
+ {
83
+ binding: PALETTE.texture,
84
+ visibility: VISIBILITY_VERTEX,
85
+ texture: { sampleType: 'unfilterable-float' },
86
+ },
87
+ vert(VERT.uniformSize),
88
+ frag,
89
+ ],
90
+ }),
91
+ };
92
+ }
93
+ /** One pane's block: what it lets through, how clear it is, and the light it is drawn for. */
94
+ writePane(slot, glass, light) {
95
+ const pane = paneScratch;
96
+ pane[0] = glass.transmission * glass.tint[0];
97
+ pane[1] = glass.transmission * glass.tint[1];
98
+ pane[2] = glass.transmission * glass.tint[2];
99
+ pane[3] = 1 - glass.frost;
100
+ this.frag.writeFloats(slot, PANE, pane);
101
+ this.frag.writeFloats(slot, LIGHT, light);
102
+ }
103
+ /** The plain group, rebuilt only when a ring has grown a new buffer under it. */
104
+ group(vertBuffer) {
105
+ if (this.plainGroup === null ||
106
+ this.groupVertBuffer !== vertBuffer ||
107
+ this.groupFragBuffer !== this.frag.buffer) {
108
+ this.cutoutGroups.clear();
109
+ this.skinnedGroups.clear();
110
+ this.groupVertBuffer = vertBuffer;
111
+ this.groupFragBuffer = this.frag.buffer;
112
+ this.plainGroup = this.device.createBindGroup({
113
+ label: 'glassTint.group',
114
+ layout: this.layouts.plain,
115
+ entries: [
116
+ { binding: VERT.uniforms, resource: { buffer: vertBuffer, size: VERT.uniformSize } },
117
+ {
118
+ binding: FRAG.uniforms,
119
+ resource: { buffer: this.frag.buffer, size: FRAG.uniformSize },
120
+ },
121
+ ],
122
+ });
123
+ }
124
+ return this.plainGroup;
125
+ }
126
+ /** A cutout pane's group, one per map, built the first time that map casts colour. */
127
+ cutoutGroup(vertBuffer, view, sampler) {
128
+ this.group(vertBuffer);
129
+ const held = this.cutoutGroups.get(view);
130
+ if (held !== undefined)
131
+ return held;
132
+ const made = this.device.createBindGroup({
133
+ label: 'glassTint.group.cutout',
134
+ layout: this.layouts.cutout,
135
+ entries: [
136
+ {
137
+ binding: CUTOUT_VERT.uniforms,
138
+ resource: { buffer: vertBuffer, size: CUTOUT_VERT.uniformSize },
139
+ },
140
+ { binding: FRAG.uniforms, resource: { buffer: this.frag.buffer, size: FRAG.uniformSize } },
141
+ { binding: CUTOUT_MAP.texture, resource: view },
142
+ { binding: CUTOUT_MAP.sampler, resource: sampler },
143
+ ],
144
+ });
145
+ this.cutoutGroups.set(view, made);
146
+ return made;
147
+ }
148
+ /** A skinned pane's group, one per palette view. */
149
+ skinnedGroup(vertBuffer, palette) {
150
+ this.group(vertBuffer);
151
+ const held = this.skinnedGroups.get(palette);
152
+ if (held !== undefined)
153
+ return held;
154
+ const made = this.device.createBindGroup({
155
+ label: 'glassTint.group.skinned',
156
+ layout: this.layouts.skinned,
157
+ entries: [
158
+ { binding: PALETTE.texture, resource: palette },
159
+ { binding: VERT.uniforms, resource: { buffer: vertBuffer, size: VERT.uniformSize } },
160
+ { binding: FRAG.uniforms, resource: { buffer: this.frag.buffer, size: FRAG.uniformSize } },
161
+ ],
162
+ });
163
+ this.skinnedGroups.set(palette, made);
164
+ return made;
165
+ }
166
+ /** The pipeline for one kind of caster, cull mode and vertex layout, cached by key. */
167
+ pipeline(cache, kind, key, present, cullMode) {
168
+ const cutout = kind === 'cutout' || kind === 'instancedCutout';
169
+ const instanced = kind === 'instanced' || kind === 'instancedCutout';
170
+ const layout = kind === 'skinned' ? this.layouts.skinned : cutout ? this.layouts.cutout : this.layouts.plain;
171
+ const vertex = kind === 'rigid'
172
+ ? GLASS_TINT_VERT_WGSL
173
+ : kind === 'instanced'
174
+ ? GLASS_TINT_INSTANCED_VERT_WGSL
175
+ : kind === 'skinned'
176
+ ? GLASS_TINT_SKINNED_VERT_WGSL
177
+ : kind === 'cutout'
178
+ ? GLASS_TINT_CUTOUT_VERT_WGSL
179
+ : GLASS_TINT_INSTANCED_CUTOUT_VERT_WGSL;
180
+ const label = `glass-tint|${kind}|${cullMode}|${key}`;
181
+ return cache.get(label, () => ({
182
+ label,
183
+ layout: this.device.createPipelineLayout({ bindGroupLayouts: [layout] }),
184
+ vertex: {
185
+ module: shaderModule(this.device, { label: `glassTint.${kind}.vert`, code: vertex }),
186
+ entryPoint: 'main',
187
+ buffers: vertexBufferLayouts(present, instanced),
188
+ },
189
+ fragment: {
190
+ module: shaderModule(this.device, {
191
+ label: cutout ? 'glassTint.cutout.frag' : 'glassTint.frag',
192
+ code: cutout ? GLASS_TINT_CUTOUT_FRAG_WGSL : GLASS_TINT_FRAG_WGSL,
193
+ }),
194
+ entryPoint: 'main',
195
+ targets: [{ format: 'rgba8unorm', blend: GLASS_TINT_BLEND }],
196
+ },
197
+ /* The depth pass's primitive state exactly — its winding included, for the reason
198
+ `depthPipeline` gives: this map is sampled, not presented. */
199
+ primitive: { topology: 'triangle-list', cullMode, frontFace: 'cw' },
200
+ }));
201
+ }
202
+ dispose() {
203
+ this.frag.dispose();
204
+ }
205
+ }
206
+ const paneScratch = new Float32Array(4);