@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
@@ -1,49 +1,106 @@
1
1
  /**
2
- * Single-cascade directional shadow map: one depth texture, one extra draw of
3
- * the static world per update.
2
+ * The sun's shadow: one depth array whose layers are the static world, its movers, and the static
3
+ * world's second occluder (the peel), drawn one at a time and sampled together.
4
4
  *
5
- * Filtering belongs to the receiving material shader, so the depth target can
6
- * stay a single-sample texture shared by every mesh pass.
5
+ * **One array rather than three maps since 4.5.0**, for the reason the lamps' twelve cubemaps
6
+ * became one: a texture unit. The lit pass binds all three through one `sampler2DArray`, which is
7
+ * what paid for the two tints glass casts with. The layers share one size and one format, so
8
+ * nothing about how each is drawn or read moved — only where it lives.
7
9
  *
8
- * Note this is a depth *render target*, not an art texture: AGENTS.md's
9
- * "zero textures" rule is about the art direction (no image assets in the
10
- * payload), and nothing here adds a byte to it.
10
+ * Filtering belongs to the receiving material shader, so the depth target can stay a
11
+ * single-sample texture shared by every mesh pass. This is a depth *render target*, not an art
12
+ * texture: AGENTS.md's "zero textures" rule is about the payload, and nothing here adds to it.
11
13
  */
12
14
  import { SHADOW_DEPTH_CLEAR } from './depthConvention.ts';
13
15
  import { DEFAULT_RENDER_QUALITY } from './renderQuality.ts';
14
16
 
15
- export class ShadowMap {
17
+ /** Where each of the sun's maps lives in the array. The shaders read these same numbers. */
18
+ export const SUN_STATIC_LAYER = 0;
19
+ export const SUN_DYNAMIC_LAYER = 1;
20
+ /** Present only when more than one static depth layer is asked for. */
21
+ export const SUN_PEELED_LAYER = 2;
22
+ /**
23
+ * Where the nearest pane's depth lives once glass has cast: after the three maps, at fixed places,
24
+ * because a shader cannot ask an array how many layers it has here — naga refuses the third
25
+ * component of an arrayed size query. A profile with no peel grows past an unused third layer to
26
+ * reach them, which is one map's memory in a profile that already declined one.
27
+ */
28
+ export const SUN_GLASS_STATIC_LAYER = 3;
29
+ export const SUN_GLASS_MOVING_LAYER = 4;
30
+
31
+ /** The three passes a scene opens, by the names `beginShadowPass` takes. */
32
+ export type SunShadowLayer = 'static' | 'static-peel' | 'dynamic';
33
+
34
+ /** The array layer a pass draws into. */
35
+ export function sunLayerIndex(layer: SunShadowLayer): number {
36
+ return layer === 'static'
37
+ ? SUN_STATIC_LAYER
38
+ : layer === 'dynamic'
39
+ ? SUN_DYNAMIC_LAYER
40
+ : SUN_PEELED_LAYER;
41
+ }
42
+
43
+ export class SunShadowArray {
16
44
  readonly size: number;
17
- readonly texture: WebGLTexture;
45
+ /** Replaced once, when glass first casts: see `growForGlass`. */
46
+ private current: WebGLTexture;
47
+ private layerCount: number;
48
+ private glass = false;
18
49
 
19
50
  private readonly framebuffer: WebGLFramebuffer;
51
+ /**
52
+ * **The peel is drawn here and copied into its layer when the pass ends.** It reads the static
53
+ * layer while it is drawn, and WebGL2 calls a texture both sampled and attached a feedback loop
54
+ * whatever layer each names — so drawing the peel into the array would draw nothing at all.
55
+ * What it costs is one more map's memory where a peel is asked for, and one blit per peel.
56
+ */
57
+ private readonly peelTexture: WebGLTexture | null;
58
+ private readonly peelFramebuffer: WebGLFramebuffer | null;
59
+ private peeling = false;
20
60
 
21
- constructor(gl: WebGL2RenderingContext, size = DEFAULT_RENDER_QUALITY.directionalShadowMapSize) {
61
+ constructor(
62
+ gl: WebGL2RenderingContext,
63
+ size = DEFAULT_RENDER_QUALITY.directionalShadowMapSize,
64
+ peeled = false,
65
+ ) {
22
66
  this.size = size;
67
+ this.layerCount = peeled ? 3 : 2;
23
68
 
24
- const texture = gl.createTexture();
25
- if (texture === null) throw new Error('ShadowMap: createTexture failed');
26
- this.texture = texture;
27
-
28
- gl.bindTexture(gl.TEXTURE_2D, texture);
29
- gl.texStorage2D(gl.TEXTURE_2D, 1, gl.DEPTH_COMPONENT24, size, size);
30
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
31
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
32
- // Clamp so anything outside the light frustum samples the border and is
33
- // treated as lit, rather than wrapping into a spurious shadow.
34
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
35
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
69
+ const texture = createSunTexture(gl, size, this.layerCount);
70
+ this.current = texture;
36
71
 
37
72
  const framebuffer = gl.createFramebuffer();
38
- if (framebuffer === null) throw new Error('ShadowMap: createFramebuffer failed');
73
+ if (framebuffer === null) throw new Error('SunShadowArray: createFramebuffer failed');
39
74
  this.framebuffer = framebuffer;
40
-
41
75
  gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
42
- gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.TEXTURE_2D, texture, 0);
76
+ gl.framebufferTextureLayer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, texture, 0, SUN_STATIC_LAYER);
43
77
  // Depth-only: without this WebGL expects a colour attachment.
44
78
  gl.drawBuffers([gl.NONE]);
45
79
  gl.readBuffer(gl.NONE);
46
80
 
81
+ if (peeled) {
82
+ const peelTexture = gl.createTexture();
83
+ const peelFramebuffer = gl.createFramebuffer();
84
+ if (peelTexture === null || peelFramebuffer === null) {
85
+ throw new Error('SunShadowArray: the peel target could not be created');
86
+ }
87
+ gl.bindTexture(gl.TEXTURE_2D, peelTexture);
88
+ gl.texStorage2D(gl.TEXTURE_2D, 1, gl.DEPTH_COMPONENT24, size, size);
89
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
90
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
91
+ gl.bindFramebuffer(gl.FRAMEBUFFER, peelFramebuffer);
92
+ gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.TEXTURE_2D, peelTexture, 0);
93
+ gl.drawBuffers([gl.NONE]);
94
+ gl.readBuffer(gl.NONE);
95
+ gl.bindTexture(gl.TEXTURE_2D, null);
96
+ this.peelTexture = peelTexture;
97
+ this.peelFramebuffer = peelFramebuffer;
98
+ } else {
99
+ this.peelTexture = null;
100
+ this.peelFramebuffer = null;
101
+ }
102
+
103
+ gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
47
104
  const status = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
48
105
  if (status !== gl.FRAMEBUFFER_COMPLETE) {
49
106
  /*
@@ -52,29 +109,121 @@ export class ShadowMap {
52
109
  * because the context cannot be lost before it exists.
53
110
  */
54
111
  if (gl.isContextLost()) return;
55
- throw new Error(`ShadowMap: framebuffer incomplete (0x${status.toString(16)})`);
112
+ throw new Error(`SunShadowArray: framebuffer incomplete (0x${status.toString(16)})`);
56
113
  }
57
114
 
58
115
  /*
59
- * **Cleared once, here, to the far plane, because nothing else says what the texture holds.**
60
- * WebGL zeroes it, and a zero compared with `LEQUAL` is an occluder at the light itself, so
61
- * a layer no pass has drawn put the whole world in shade. ANGLE on Vulkan hands the same
62
- * texture back reading 1, which is lit, and that is what every capture here ran on: a scene
63
- * with a second depth layer it never peeled lost its sun only under ANGLE's GL backend, the
64
- * one Chrome on Linux runs by default. The frame's clear value is borrowed and put back.
116
+ * **Every layer cleared once, here, to the far plane, because nothing else says what it
117
+ * holds.** WebGL zeroes the texture, and a zero compared with `LEQUAL` is an occluder at the
118
+ * light itself, so a layer no pass has drawn put the whole world in shade. ANGLE on Vulkan
119
+ * hands the same texture back reading 1, which is lit, and that is what every capture here
120
+ * ran on: a scene with a second depth layer it never peeled lost its sun only under ANGLE's
121
+ * GL backend, the one Chrome on Linux runs by default. The frame's clear value is put back.
65
122
  */
66
123
  const frameClear = gl.getParameter(gl.DEPTH_CLEAR_VALUE) as number;
67
124
  gl.clearDepth(SHADOW_DEPTH_CLEAR);
68
- gl.clear(gl.DEPTH_BUFFER_BIT);
125
+ gl.viewport(0, 0, size, size);
126
+ for (let layer = 0; layer < this.layers; layer++) {
127
+ gl.framebufferTextureLayer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, texture, 0, layer);
128
+ gl.clear(gl.DEPTH_BUFFER_BIT);
129
+ }
69
130
  gl.clearDepth(frameClear);
70
131
 
71
132
  gl.bindFramebuffer(gl.FRAMEBUFFER, null);
72
- gl.bindTexture(gl.TEXTURE_2D, null);
133
+ gl.bindTexture(gl.TEXTURE_2D_ARRAY, null);
134
+ }
135
+
136
+ get texture(): WebGLTexture {
137
+ return this.current;
138
+ }
139
+
140
+ get layers(): number {
141
+ return this.layerCount;
142
+ }
143
+
144
+ /** Whether the two glass layers exist: from the first glass a sun pass is offered, for good. */
145
+ get hasGlass(): boolean {
146
+ return this.glass;
73
147
  }
74
148
 
75
- /** Bind as the render target and clear. Caller then draws the casters. */
76
- begin(gl: WebGL2RenderingContext): void {
149
+ /** The glass depth layer beside a map: fixed places after the three maps. */
150
+ glassLayer(which: 'static' | 'dynamic'): number {
151
+ return which === 'static' ? SUN_GLASS_STATIC_LAYER : SUN_GLASS_MOVING_LAYER;
152
+ }
153
+
154
+ /**
155
+ * Add the two glass layers, once, carrying every layer already held across.
156
+ *
157
+ * **Carried rather than re-baked**, because the sun's static map is the consumer's to bake and
158
+ * it may bake it once: the glass that grows the array arrives in the very pass that drew that
159
+ * map, so a copy is what keeps it. A depth blit per layer, same size and format, and the glass
160
+ * layers cleared to the far plane — nothing nearer than the sun until a pane is drawn there.
161
+ * Returns whether it grew, so the caller knows every bind of the old texture is stale.
162
+ */
163
+ growForGlass(gl: WebGL2RenderingContext): boolean {
164
+ if (this.glass) return false;
165
+ const old = this.current;
166
+ const oldLayers = this.layerCount;
167
+ const layers = SUN_GLASS_MOVING_LAYER + 1;
168
+ const texture = createSunTexture(gl, this.size, layers);
169
+ const read = gl.createFramebuffer();
170
+ if (read === null) throw new Error('SunShadowArray: createFramebuffer failed');
171
+ gl.bindFramebuffer(gl.READ_FRAMEBUFFER, read);
172
+ gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, this.framebuffer);
173
+ for (let layer = 0; layer < oldLayers; layer++) {
174
+ gl.framebufferTextureLayer(gl.READ_FRAMEBUFFER, gl.DEPTH_ATTACHMENT, old, 0, layer);
175
+ gl.framebufferTextureLayer(gl.DRAW_FRAMEBUFFER, gl.DEPTH_ATTACHMENT, texture, 0, layer);
176
+ gl.blitFramebuffer(
177
+ 0,
178
+ 0,
179
+ this.size,
180
+ this.size,
181
+ 0,
182
+ 0,
183
+ this.size,
184
+ this.size,
185
+ gl.DEPTH_BUFFER_BIT,
186
+ gl.NEAREST,
187
+ );
188
+ }
189
+ for (let layer = oldLayers; layer < layers; layer++) {
190
+ gl.framebufferTextureLayer(gl.DRAW_FRAMEBUFFER, gl.DEPTH_ATTACHMENT, texture, 0, layer);
191
+ gl.clearBufferfv(gl.DEPTH, 0, FAR_PLANE);
192
+ }
193
+ gl.bindFramebuffer(gl.READ_FRAMEBUFFER, null);
194
+ gl.bindFramebuffer(gl.FRAMEBUFFER, null);
195
+ gl.deleteFramebuffer(read);
196
+ gl.deleteTexture(old);
197
+ this.current = texture;
198
+ this.layerCount = layers;
199
+ this.glass = true;
200
+ return true;
201
+ }
202
+
203
+ /** Bind `layer` as the render target and clear it. Caller then draws the casters. */
204
+ begin(gl: WebGL2RenderingContext, layer: SunShadowLayer): void {
205
+ this.peeling = layer === 'static-peel' && this.peelFramebuffer !== null;
206
+ if (this.peeling) {
207
+ gl.bindFramebuffer(gl.FRAMEBUFFER, this.peelFramebuffer);
208
+ } else {
209
+ this.attach(gl, sunLayerIndex(layer));
210
+ }
211
+ this.clearAndRaster(gl);
212
+ }
213
+
214
+ /** Bind a glass depth layer as the render target and clear it: where the nearest pane is. */
215
+ beginGlass(gl: WebGL2RenderingContext, which: 'static' | 'dynamic'): void {
216
+ this.peeling = false;
217
+ this.attach(gl, this.glassLayer(which));
218
+ this.clearAndRaster(gl);
219
+ }
220
+
221
+ private attach(gl: WebGL2RenderingContext, layer: number): void {
77
222
  gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer);
223
+ gl.framebufferTextureLayer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, this.current, 0, layer);
224
+ }
225
+
226
+ private clearAndRaster(gl: WebGL2RenderingContext): void {
78
227
  gl.viewport(0, 0, this.size, this.size);
79
228
  gl.clear(gl.DEPTH_BUFFER_BIT);
80
229
  /*
@@ -93,11 +242,56 @@ export class ShadowMap {
93
242
  end(gl: WebGL2RenderingContext): void {
94
243
  gl.disable(gl.POLYGON_OFFSET_FILL);
95
244
  gl.cullFace(gl.BACK);
245
+ if (this.peeling) {
246
+ /* The peel into its layer: same size, same format, so a nearest depth blit is a copy. */
247
+ gl.bindFramebuffer(gl.READ_FRAMEBUFFER, this.peelFramebuffer);
248
+ gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, this.framebuffer);
249
+ gl.framebufferTextureLayer(
250
+ gl.DRAW_FRAMEBUFFER,
251
+ gl.DEPTH_ATTACHMENT,
252
+ this.current,
253
+ 0,
254
+ SUN_PEELED_LAYER,
255
+ );
256
+ gl.blitFramebuffer(
257
+ 0,
258
+ 0,
259
+ this.size,
260
+ this.size,
261
+ 0,
262
+ 0,
263
+ this.size,
264
+ this.size,
265
+ gl.DEPTH_BUFFER_BIT,
266
+ gl.NEAREST,
267
+ );
268
+ gl.bindFramebuffer(gl.READ_FRAMEBUFFER, null);
269
+ this.peeling = false;
270
+ }
96
271
  gl.bindFramebuffer(gl.FRAMEBUFFER, null);
97
272
  }
98
273
 
99
274
  dispose(gl: WebGL2RenderingContext): void {
100
275
  gl.deleteFramebuffer(this.framebuffer);
101
- gl.deleteTexture(this.texture);
276
+ gl.deleteTexture(this.current);
277
+ if (this.peelFramebuffer !== null) gl.deleteFramebuffer(this.peelFramebuffer);
278
+ if (this.peelTexture !== null) gl.deleteTexture(this.peelTexture);
102
279
  }
103
280
  }
281
+
282
+ /** The far plane, as `clearBufferfv` takes it: a layer no pane has reached is clear of glass. */
283
+ const FAR_PLANE = new Float32Array([SHADOW_DEPTH_CLEAR]);
284
+
285
+ function createSunTexture(gl: WebGL2RenderingContext, size: number, layers: number): WebGLTexture {
286
+ const texture = gl.createTexture();
287
+ if (texture === null) throw new Error('SunShadowArray: createTexture failed');
288
+ gl.bindTexture(gl.TEXTURE_2D_ARRAY, texture);
289
+ gl.texStorage3D(gl.TEXTURE_2D_ARRAY, 1, gl.DEPTH_COMPONENT24, size, size, layers);
290
+ gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
291
+ gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
292
+ // Clamp so anything outside the light frustum samples the border and is
293
+ // treated as lit, rather than wrapping into a spurious shadow.
294
+ gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
295
+ gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
296
+ return texture;
297
+ }
@@ -0,0 +1,86 @@
1
+ /**
2
+ * What the sun's light keeps of itself through glass: an RGBA8 array of two layers, static and
3
+ * moving, beside the sun's depth array. RGB is the product of every pane's
4
+ * `transmission × (1 − F) × tint`, A the product of their clarities (`glassShadow.ts`).
5
+ *
6
+ * **Cleared to white and drawn with a multiplying blend and no depth test**, so every pane on a ray
7
+ * contributes and the order they come in cannot matter. **Mipmapped and filtered**, unlike the
8
+ * depths beside it: a frosted pane's colour is read at the level its spread asks for, so a wide
9
+ * spread is smooth rather than a scatter of taps — the chain is rebuilt after every draw of a layer.
10
+ * `'half'` makes it half the depth map's size and nothing else changes: the light matrix is the
11
+ * same, so a texel is a texel of the same place, only larger.
12
+ */
13
+ export class SunGlassTint {
14
+ readonly size: number;
15
+ readonly texture: WebGLTexture;
16
+ private readonly framebuffer: WebGLFramebuffer;
17
+
18
+ constructor(gl: WebGL2RenderingContext, size: number) {
19
+ this.size = size;
20
+ const texture = gl.createTexture();
21
+ const framebuffer = gl.createFramebuffer();
22
+ if (texture === null || framebuffer === null) {
23
+ throw new Error('SunGlassTint: the tint target could not be created');
24
+ }
25
+ this.texture = texture;
26
+ this.framebuffer = framebuffer;
27
+ gl.bindTexture(gl.TEXTURE_2D_ARRAY, texture);
28
+ gl.texStorage3D(gl.TEXTURE_2D_ARRAY, mipLevels(size), gl.RGBA8, size, size, 2);
29
+ gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);
30
+ gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
31
+ gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
32
+ gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
33
+ gl.bindTexture(gl.TEXTURE_2D_ARRAY, null);
34
+ /* Both layers born clear: a layer no pane has reached lets everything through. */
35
+ gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
36
+ for (let layer = 0; layer < 2; layer++) {
37
+ gl.framebufferTextureLayer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, texture, 0, layer);
38
+ gl.clearBufferfv(gl.COLOR, 0, CLEAR);
39
+ }
40
+ gl.bindFramebuffer(gl.FRAMEBUFFER, null);
41
+ this.fillMips(gl);
42
+ }
43
+
44
+ /** Bind `layer` (0 static, 1 moving), clear it to white, and set the multiplying state. */
45
+ begin(gl: WebGL2RenderingContext, layer: number): void {
46
+ gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer);
47
+ gl.framebufferTextureLayer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, this.texture, 0, layer);
48
+ gl.viewport(0, 0, this.size, this.size);
49
+ gl.clearBufferfv(gl.COLOR, 0, CLEAR);
50
+ gl.disable(gl.DEPTH_TEST);
51
+ gl.depthMask(false);
52
+ gl.disable(gl.POLYGON_OFFSET_FILL);
53
+ gl.enable(gl.BLEND);
54
+ gl.blendEquation(gl.FUNC_ADD);
55
+ gl.blendFuncSeparate(gl.DST_COLOR, gl.ZERO, gl.DST_ALPHA, gl.ZERO);
56
+ }
57
+
58
+ /** Put back what `begin` changed, and rebuild the mip chain the frost reads. */
59
+ end(gl: WebGL2RenderingContext): void {
60
+ gl.disable(gl.BLEND);
61
+ gl.depthMask(true);
62
+ gl.enable(gl.DEPTH_TEST);
63
+ gl.bindFramebuffer(gl.FRAMEBUFFER, null);
64
+ this.fillMips(gl);
65
+ }
66
+
67
+ /** Every level from level 0, both layers: bound on the active unit and handed back. */
68
+ private fillMips(gl: WebGL2RenderingContext): void {
69
+ const previous = gl.getParameter(gl.TEXTURE_BINDING_2D_ARRAY) as WebGLTexture | null;
70
+ gl.bindTexture(gl.TEXTURE_2D_ARRAY, this.texture);
71
+ gl.generateMipmap(gl.TEXTURE_2D_ARRAY);
72
+ gl.bindTexture(gl.TEXTURE_2D_ARRAY, previous);
73
+ }
74
+
75
+ dispose(gl: WebGL2RenderingContext): void {
76
+ gl.deleteFramebuffer(this.framebuffer);
77
+ gl.deleteTexture(this.texture);
78
+ }
79
+ }
80
+
81
+ const CLEAR = new Float32Array([1, 1, 1, 1]);
82
+
83
+ /** A full chain down to one texel. */
84
+ function mipLevels(size: number): number {
85
+ return Math.floor(Math.log2(Math.max(1, size))) + 1;
86
+ }
@@ -0,0 +1,132 @@
1
+ /**
2
+ * What each layer of a surface array does beyond its picture: rooms behind its windows, windows that
3
+ * light by night, wear, animation, and whether rain wets it.
4
+ *
5
+ * **Per layer, because a city region is one mesh.** Its facades are told apart by the layer each
6
+ * vertex carries (`MeshData.layers`), so an effect's parameters cannot be a per-draw uniform — two facades of one
7
+ * draw would share them. They are a table beside the array, six texels a layer, read at the vertex's
8
+ * layer with `texelFetch`, and they bind whenever the array does.
9
+ *
10
+ * **Everything off is all zeros**, so a layer a scene names no effect for — and every layer of an
11
+ * array given no table — costs the shader one size query and nothing else.
12
+ *
13
+ * Layout, six RGBA texels a layer, half floats on the GPU (every whole number here is under 2,048,
14
+ * which a half holds exactly):
15
+ * - 0: window cells across and up, glass mask (1: glass where the albedo's alpha is 0), seed;
16
+ * - 1: room layer (0: none; the layer plus one), room depth (0 to 1 of a cell), share of rooms lit,
17
+ * window glow;
18
+ * - 2: window colour, then 1 where rain leaves the surface dry;
19
+ * - 3: dust, grime, streaks, the metres over which wear fades out with distance (0: never);
20
+ * - 4: scroll along u and v in repeats a second, pulse rate in hertz, pulse depth;
21
+ * - 5: flicker, flipbook frames, flipbook frames a second, the metres past which emission fades
22
+ * (0: never).
23
+ */
24
+
25
+ /** Texels a layer takes in the table. */
26
+ export const SURFACE_EFFECT_TEXELS = 6;
27
+
28
+ export interface SurfaceLayerEffect {
29
+ /**
30
+ * Windows across and up one repeat of the layer, for everything that works window by window:
31
+ * rooms, lit windows. With `glass`, a window is a hole in the albedo — glass where its alpha is 0,
32
+ * wall where it is 1 — which is how a facade is drawn, and the other way round would lose the
33
+ * wall's colour, which does not survive an upload at alpha 0.
34
+ */
35
+ readonly windows?: {
36
+ readonly cells: readonly [number, number];
37
+ readonly glass?: boolean;
38
+ /** Varies which windows light and which rooms show, between facades wearing one layer. */
39
+ readonly seed?: number;
40
+ /** How bright a lit window glows, 0 for none. */
41
+ readonly glow?: number;
42
+ /**
43
+ * The light a window shows where there is no room behind it. **A vertex that names an emissive
44
+ * colour overrides it** — the window's colour, or a tint on the room where there is one — which
45
+ * is how buildings sharing a layer each keep their own light: a window layer's vertex emissive
46
+ * is otherwise unused, so the colour there, strength folded in, is free to say it.
47
+ */
48
+ readonly colour?: readonly [number, number, number];
49
+ };
50
+ /** A room seen in parallax behind each window, drawn from another layer of the same array. */
51
+ readonly interior?: {
52
+ readonly roomLayer: number;
53
+ /** How deep a room is, as a share of a window's width. 0.5 unless stated. */
54
+ readonly depth?: number;
55
+ /** The share of rooms lit, 0 to 1. 1 unless stated. */
56
+ readonly lit?: number;
57
+ };
58
+ /** Procedural wear, each 0 to 1, fading out past `fade` metres (never, unless stated). */
59
+ readonly wear?: {
60
+ readonly dust?: number;
61
+ readonly grime?: number;
62
+ readonly streaks?: number;
63
+ readonly fade?: number;
64
+ };
65
+ /** Motion on the caller's clock (`Environment.surfaceTime`), so a held clock holds it. */
66
+ readonly animation?: {
67
+ /** Repeats a second along u and v. */
68
+ readonly scroll?: readonly [number, number];
69
+ /** Emission rising and falling: hertz, and how far it falls, 0 to 1. */
70
+ readonly pulse?: readonly [number, number];
71
+ /** Emission stuttering, 0 to 1. */
72
+ readonly flicker?: number;
73
+ /** Successive layers as frames: how many from this one, and how fast. */
74
+ readonly flipbook?: readonly [number, number];
75
+ /** Metres past which emission fades out, 0 for never. */
76
+ readonly fade?: number;
77
+ };
78
+ /** Rain leaves it dry: a covered walkway, an interior floor. */
79
+ readonly dry?: boolean;
80
+ }
81
+
82
+ /**
83
+ * The table for `effects`, one entry a layer (`undefined` for a layer with none), as floats: six
84
+ * RGBA texels a layer, a row a layer.
85
+ */
86
+ export function packSurfaceEffects(
87
+ effects: readonly (SurfaceLayerEffect | undefined)[],
88
+ ): Float32Array {
89
+ const out = new Float32Array(effects.length * SURFACE_EFFECT_TEXELS * 4);
90
+ effects.forEach((effect, layer) => {
91
+ if (effect === undefined) return;
92
+ const at = layer * SURFACE_EFFECT_TEXELS * 4;
93
+ const windows = effect.windows;
94
+ if (windows !== undefined) {
95
+ out[at] = windows.cells[0];
96
+ out[at + 1] = windows.cells[1];
97
+ out[at + 2] = windows.glass === true ? 1 : 0;
98
+ out[at + 3] = windows.seed ?? 0;
99
+ out[at + 7] = windows.glow ?? 0;
100
+ const colour = windows.colour ?? [1, 0.82, 0.55];
101
+ out[at + 8] = colour[0];
102
+ out[at + 9] = colour[1];
103
+ out[at + 10] = colour[2];
104
+ }
105
+ const interior = effect.interior;
106
+ if (interior !== undefined) {
107
+ out[at + 4] = interior.roomLayer + 1;
108
+ out[at + 5] = interior.depth ?? 0.5;
109
+ out[at + 6] = interior.lit ?? 1;
110
+ }
111
+ out[at + 11] = effect.dry === true ? 1 : 0;
112
+ const wear = effect.wear;
113
+ if (wear !== undefined) {
114
+ out[at + 12] = wear.dust ?? 0;
115
+ out[at + 13] = wear.grime ?? 0;
116
+ out[at + 14] = wear.streaks ?? 0;
117
+ out[at + 15] = wear.fade ?? 0;
118
+ }
119
+ const animation = effect.animation;
120
+ if (animation !== undefined) {
121
+ out[at + 16] = animation.scroll?.[0] ?? 0;
122
+ out[at + 17] = animation.scroll?.[1] ?? 0;
123
+ out[at + 18] = animation.pulse?.[0] ?? 0;
124
+ out[at + 19] = animation.pulse?.[1] ?? 0;
125
+ out[at + 20] = animation.flicker ?? 0;
126
+ out[at + 21] = animation.flipbook?.[0] ?? 0;
127
+ out[at + 22] = animation.flipbook?.[1] ?? 0;
128
+ out[at + 23] = animation.fade ?? 0;
129
+ }
130
+ });
131
+ return out;
132
+ }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * The scene's surface clock and weather, decided once for both backends: the four numbers the lit
3
+ * stage reads as `uSurfaceScene`. Each backend only uploads them, so the two cannot disagree about
4
+ * what a wetness of 1.4 means — it means 1, as a share of lit windows past one means all of them.
5
+ */
6
+ import type { Environment } from './backend/webgl2/renderer.ts';
7
+
8
+ const unit = (value: number | undefined): number => Math.min(1, Math.max(0, value ?? 0));
9
+
10
+ /** Fill `out`: seconds on the caller's clock, wetness, the lit share, the late share. */
11
+ export function resolveSurfaceScene(env: Environment, out: Float32Array): void {
12
+ out[0] = env.surfaceTime ?? 0;
13
+ out[1] = unit(env.wetness);
14
+ out[2] = unit(env.litWindows);
15
+ out[3] = unit(env.lateWindows);
16
+ }