@driftengine/core 3.63.0 → 4.1.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 (428) hide show
  1. package/CHANGELOG.json +122 -0
  2. package/NOTICE +20 -0
  3. package/README.md +1 -1
  4. package/dist/core/loop.js +18 -2
  5. package/dist/core/storage.js +5 -0
  6. package/dist/geometry/meshBuilder.d.ts +1 -1
  7. package/dist/geometry/meshBuilder.js +6 -2
  8. package/dist/index.d.ts +69 -4
  9. package/dist/index.js +112 -2
  10. package/dist/input/input.js +6 -1
  11. package/dist/math/exact.d.ts +13 -3
  12. package/dist/math/exact.js +68 -3
  13. package/dist/math/frustum.d.ts +17 -2
  14. package/dist/math/frustum.js +24 -0
  15. package/dist/render/backend/api.d.ts +34 -1
  16. package/dist/render/backend/api.js +22 -1
  17. package/dist/render/backend/budget.d.ts +7 -6
  18. package/dist/render/backend/budget.js +7 -6
  19. package/dist/render/backend/createRenderer.d.ts +57 -0
  20. package/dist/render/backend/createRenderer.js +51 -2
  21. package/dist/render/backend/materialChanges.d.ts +41 -0
  22. package/dist/render/backend/materialChanges.js +38 -0
  23. package/dist/render/backend/select.js +8 -0
  24. package/dist/render/backend/webgl2/renderer.d.ts +80 -1
  25. package/dist/render/backend/webgl2/renderer.js +208 -10
  26. package/dist/render/backend/webgpu/buffers.d.ts +20 -1
  27. package/dist/render/backend/webgpu/buffers.js +116 -58
  28. package/dist/render/backend/webgpu/fieldCompose.d.ts +156 -0
  29. package/dist/render/backend/webgpu/fieldCompose.js +396 -0
  30. package/dist/render/backend/webgpu/giFieldPass.d.ts +66 -0
  31. package/dist/render/backend/webgpu/giFieldPass.js +137 -0
  32. package/dist/render/backend/webgpu/gpuDrivenPass.d.ts +728 -0
  33. package/dist/render/backend/webgpu/gpuDrivenPass.js +2473 -0
  34. package/dist/render/backend/webgpu/linePass.d.ts +4 -14
  35. package/dist/render/backend/webgpu/linePass.js +4 -33
  36. package/dist/render/backend/webgpu/probeBake.d.ts +148 -0
  37. package/dist/render/backend/webgpu/probeBake.js +493 -0
  38. package/dist/render/backend/webgpu/renderer.d.ts +379 -24
  39. package/dist/render/backend/webgpu/renderer.js +1369 -76
  40. package/dist/render/backend/webgpu/scatterPass.js +15 -1
  41. package/dist/render/backend/webgpu/surfaceTexturePass.d.ts +19 -9
  42. package/dist/render/backend/webgpu/surfaceTexturePass.js +58 -32
  43. package/dist/render/causticsRenderer.d.ts +3 -1
  44. package/dist/render/causticsRenderer.js +4 -1
  45. package/dist/render/depthConvention.d.ts +12 -0
  46. package/dist/render/depthConvention.js +14 -0
  47. package/dist/render/deviceFeatures.d.ts +32 -0
  48. package/dist/render/deviceFeatures.js +41 -0
  49. package/dist/render/fog.d.ts +79 -0
  50. package/dist/render/fog.js +138 -0
  51. package/dist/render/frame/alias.d.ts +30 -0
  52. package/dist/render/frame/alias.js +61 -0
  53. package/dist/render/frame/deps.d.ts +37 -0
  54. package/dist/render/frame/deps.js +122 -0
  55. package/dist/render/frame/flushGraph.d.ts +43 -0
  56. package/dist/render/frame/flushGraph.js +84 -0
  57. package/dist/render/frame/graphSchedule.d.ts +49 -0
  58. package/dist/render/frame/graphSchedule.js +120 -0
  59. package/dist/render/frame/index.d.ts +15 -0
  60. package/dist/render/frame/index.js +8 -0
  61. package/dist/render/frame/lifetime.d.ts +24 -0
  62. package/dist/render/frame/lifetime.js +36 -0
  63. package/dist/render/frame/passTimings.d.ts +42 -0
  64. package/dist/render/frame/passTimings.js +38 -0
  65. package/dist/render/frame/validate.d.ts +15 -0
  66. package/dist/render/frame/validate.js +22 -0
  67. package/dist/render/frame/virtual.d.ts +44 -0
  68. package/dist/render/frame/virtual.js +61 -0
  69. package/dist/render/gi/chain.d.ts +82 -0
  70. package/dist/render/gi/chain.js +135 -0
  71. package/dist/render/gi/denoise.d.ts +114 -0
  72. package/dist/render/gi/denoise.js +169 -0
  73. package/dist/render/gi/fieldScene.d.ts +125 -0
  74. package/dist/render/gi/fieldScene.js +219 -0
  75. package/dist/render/gi/globalField.d.ts +157 -0
  76. package/dist/render/gi/globalField.js +373 -0
  77. package/dist/render/gi/probeGridFit.d.ts +24 -0
  78. package/dist/render/gi/probeGridFit.js +71 -0
  79. package/dist/render/gi/probeTrace.d.ts +82 -0
  80. package/dist/render/gi/probeTrace.js +128 -0
  81. package/dist/render/gi/probeVolume.d.ts +136 -0
  82. package/dist/render/gi/probeVolume.js +191 -0
  83. package/dist/render/gi/reflection.d.ts +73 -0
  84. package/dist/render/gi/reflection.js +109 -0
  85. package/dist/render/gi/traceField.d.ts +91 -0
  86. package/dist/render/gi/traceField.js +129 -0
  87. package/dist/render/gi/traceScreen.d.ts +68 -0
  88. package/dist/render/gi/traceScreen.js +82 -0
  89. package/dist/render/gpuScene.d.ts +38 -0
  90. package/dist/render/gpuScene.js +41 -0
  91. package/dist/render/gpudriven/alphaTest.d.ts +28 -0
  92. package/dist/render/gpudriven/alphaTest.js +32 -0
  93. package/dist/render/gpudriven/clusterUpload.d.ts +59 -0
  94. package/dist/render/gpudriven/clusterUpload.js +75 -0
  95. package/dist/render/gpudriven/clusterWorld.d.ts +57 -0
  96. package/dist/render/gpudriven/clusterWorld.js +155 -0
  97. package/dist/render/gpudriven/compact.d.ts +51 -0
  98. package/dist/render/gpudriven/compact.js +68 -0
  99. package/dist/render/gpudriven/cullClusters.d.ts +129 -0
  100. package/dist/render/gpudriven/cullClusters.js +237 -0
  101. package/dist/render/gpudriven/decodeTables.d.ts +64 -0
  102. package/dist/render/gpudriven/decodeTables.js +219 -0
  103. package/dist/render/gpudriven/frustum.d.ts +25 -0
  104. package/dist/render/gpudriven/frustum.js +59 -0
  105. package/dist/render/gpudriven/geometryHarness.d.ts +15 -0
  106. package/dist/render/gpudriven/geometryHarness.js +31 -0
  107. package/dist/render/gpudriven/hzb.d.ts +60 -0
  108. package/dist/render/gpudriven/hzb.js +97 -0
  109. package/dist/render/gpudriven/ibl.d.ts +85 -0
  110. package/dist/render/gpudriven/ibl.js +129 -0
  111. package/dist/render/gpudriven/indirect.d.ts +67 -0
  112. package/dist/render/gpudriven/indirect.js +77 -0
  113. package/dist/render/gpudriven/instances.d.ts +43 -0
  114. package/dist/render/gpudriven/instances.js +172 -0
  115. package/dist/render/gpudriven/lit.d.ts +165 -0
  116. package/dist/render/gpudriven/lit.js +165 -0
  117. package/dist/render/gpudriven/lodCut.d.ts +28 -0
  118. package/dist/render/gpudriven/lodCut.js +38 -0
  119. package/dist/render/gpudriven/materialBin.d.ts +60 -0
  120. package/dist/render/gpudriven/materialBin.js +108 -0
  121. package/dist/render/gpudriven/materialTable.d.ts +88 -0
  122. package/dist/render/gpudriven/materialTable.js +115 -0
  123. package/dist/render/gpudriven/pipeline.d.ts +135 -0
  124. package/dist/render/gpudriven/pipeline.js +237 -0
  125. package/dist/render/gpudriven/rangeAlloc.d.ts +50 -0
  126. package/dist/render/gpudriven/rangeAlloc.js +112 -0
  127. package/dist/render/gpudriven/sceneUpload.d.ts +96 -0
  128. package/dist/render/gpudriven/sceneUpload.js +115 -0
  129. package/dist/render/gpudriven/shadeBins.d.ts +92 -0
  130. package/dist/render/gpudriven/shadeBins.js +182 -0
  131. package/dist/render/gpudriven/shadow.d.ts +136 -0
  132. package/dist/render/gpudriven/shadow.js +224 -0
  133. package/dist/render/gpudriven/shadowCamera.d.ts +87 -0
  134. package/dist/render/gpudriven/shadowCamera.js +345 -0
  135. package/dist/render/gpudriven/streamScene.d.ts +194 -0
  136. package/dist/render/gpudriven/streamScene.js +459 -0
  137. package/dist/render/gpudriven/surfaceFrame.d.ts +25 -0
  138. package/dist/render/gpudriven/surfaceFrame.js +51 -0
  139. package/dist/render/gpudriven/twoPhase.d.ts +122 -0
  140. package/dist/render/gpudriven/twoPhase.js +160 -0
  141. package/dist/render/gpudriven/visbuffer.d.ts +25 -0
  142. package/dist/render/gpudriven/visbuffer.js +37 -0
  143. package/dist/render/halfFloat.d.ts +10 -0
  144. package/dist/render/halfFloat.js +23 -4
  145. package/dist/render/inference/attentionTiled.d.ts +27 -0
  146. package/dist/render/inference/attentionTiled.js +117 -0
  147. package/dist/render/inference/denseKernels.d.ts +29 -0
  148. package/dist/render/inference/denseKernels.js +312 -0
  149. package/dist/render/inference/device.d.ts +21 -0
  150. package/dist/render/inference/device.js +32 -0
  151. package/dist/render/inference/deviceGraph.d.ts +34 -0
  152. package/dist/render/inference/deviceGraph.js +17 -0
  153. package/dist/render/inference/fuse.d.ts +15 -0
  154. package/dist/render/inference/fuse.js +37 -0
  155. package/dist/render/inference/kernelKit.d.ts +52 -0
  156. package/dist/render/inference/kernelKit.js +70 -0
  157. package/dist/render/inference/kernels.d.ts +3 -0
  158. package/dist/render/inference/kernels.js +11 -0
  159. package/dist/render/inference/plan.d.ts +13 -0
  160. package/dist/render/inference/plan.js +18 -0
  161. package/dist/render/inference/reuse.d.ts +39 -0
  162. package/dist/render/inference/reuse.js +65 -0
  163. package/dist/render/inference/runner.d.ts +31 -0
  164. package/dist/render/inference/runner.js +175 -0
  165. package/dist/render/inference/schedule.d.ts +39 -0
  166. package/dist/render/inference/schedule.js +59 -0
  167. package/dist/render/inference/shapeKernels.d.ts +10 -0
  168. package/dist/render/inference/shapeKernels.js +80 -0
  169. package/dist/render/inference/spatialKernels.d.ts +21 -0
  170. package/dist/render/inference/spatialKernels.js +179 -0
  171. package/dist/render/inference/splatRaster.wgsl.d.ts +75 -0
  172. package/dist/render/inference/splatRaster.wgsl.js +337 -0
  173. package/dist/render/inference/splatTiles.d.ts +79 -0
  174. package/dist/render/inference/splatTiles.js +161 -0
  175. package/dist/render/lineBatch.d.ts +2 -5
  176. package/dist/render/lineBatch.js +4 -24
  177. package/dist/render/mesh.d.ts +4 -0
  178. package/dist/render/mesh.js +45 -1
  179. package/dist/render/pass.d.ts +145 -8
  180. package/dist/render/prefilterEnvMap.d.ts +22 -0
  181. package/dist/render/prefilterEnvMap.js +31 -0
  182. package/dist/render/readback.d.ts +30 -0
  183. package/dist/render/readback.js +47 -0
  184. package/dist/render/recon/clamp.d.ts +36 -0
  185. package/dist/render/recon/clamp.js +92 -0
  186. package/dist/render/recon/disocclusion.d.ts +60 -0
  187. package/dist/render/recon/disocclusion.js +109 -0
  188. package/dist/render/recon/frameGen.d.ts +81 -0
  189. package/dist/render/recon/frameGen.js +150 -0
  190. package/dist/render/recon/frameSizes.d.ts +34 -0
  191. package/dist/render/recon/frameSizes.js +58 -0
  192. package/dist/render/recon/jitter.d.ts +60 -0
  193. package/dist/render/recon/jitter.js +118 -0
  194. package/dist/render/recon/motionVectors.d.ts +55 -0
  195. package/dist/render/recon/motionVectors.js +107 -0
  196. package/dist/render/recon/reproject.d.ts +41 -0
  197. package/dist/render/recon/reproject.js +131 -0
  198. package/dist/render/recon/resolve.d.ts +90 -0
  199. package/dist/render/recon/resolve.js +313 -0
  200. package/dist/render/recon/tier.d.ts +24 -0
  201. package/dist/render/recon/tier.js +8 -0
  202. package/dist/render/reflectionProbe.d.ts +26 -0
  203. package/dist/render/reflectionProbe.js +33 -0
  204. package/dist/render/renderQuality.d.ts +69 -0
  205. package/dist/render/renderQuality.js +22 -0
  206. package/dist/render/screenSpaceReflection.d.ts +23 -0
  207. package/dist/render/screenSpaceReflection.js +22 -3
  208. package/dist/render/sdfFont.js +2 -0
  209. package/dist/render/segmentQuads.d.ts +13 -0
  210. package/dist/render/segmentQuads.js +32 -0
  211. package/dist/render/shaders/flat/lobes.d.ts +27 -0
  212. package/dist/render/shaders/flat/lobes.js +33 -3
  213. package/dist/render/shaders/flat/preamble.d.ts +7 -0
  214. package/dist/render/shaders/flat/preamble.js +24 -1
  215. package/dist/render/shaders/flat/probeGrid.d.ts +1 -1
  216. package/dist/render/shaders/flat/probeGrid.js +71 -2
  217. package/dist/render/shaders/generated/flat.wgsl.d.ts +48 -8
  218. package/dist/render/shaders/generated/flat.wgsl.js +64 -24
  219. package/dist/render/shaders/gi/composeField.wgsl.d.ts +56 -0
  220. package/dist/render/shaders/gi/composeField.wgsl.js +231 -0
  221. package/dist/render/shaders/gi/marchField.wgsl.d.ts +30 -0
  222. package/dist/render/shaders/gi/marchField.wgsl.js +141 -0
  223. package/dist/render/shaders/gi/probeBake.wgsl.d.ts +113 -0
  224. package/dist/render/shaders/gi/probeBake.wgsl.js +635 -0
  225. package/dist/render/shaders/gi/probeTrace.wgsl.d.ts +72 -0
  226. package/dist/render/shaders/gi/probeTrace.wgsl.js +261 -0
  227. package/dist/render/shaders/gi/probeVolume.wgsl.d.ts +49 -0
  228. package/dist/render/shaders/gi/probeVolume.wgsl.js +242 -0
  229. package/dist/render/shaders/gi/sampleField.wgsl.d.ts +32 -0
  230. package/dist/render/shaders/gi/sampleField.wgsl.js +134 -0
  231. package/dist/render/shaders/gpudriven/blendRaster.wgsl.d.ts +26 -0
  232. package/dist/render/shaders/gpudriven/blendRaster.wgsl.js +339 -0
  233. package/dist/render/shaders/gpudriven/compact.wgsl.d.ts +29 -0
  234. package/dist/render/shaders/gpudriven/compact.wgsl.js +58 -0
  235. package/dist/render/shaders/gpudriven/cull.wgsl.d.ts +90 -0
  236. package/dist/render/shaders/gpudriven/cull.wgsl.js +423 -0
  237. package/dist/render/shaders/gpudriven/decode.wgsl.d.ts +72 -0
  238. package/dist/render/shaders/gpudriven/decode.wgsl.js +349 -0
  239. package/dist/render/shaders/gpudriven/environment.wgsl.d.ts +44 -0
  240. package/dist/render/shaders/gpudriven/environment.wgsl.js +137 -0
  241. package/dist/render/shaders/gpudriven/fog.wgsl.d.ts +28 -0
  242. package/dist/render/shaders/gpudriven/fog.wgsl.js +107 -0
  243. package/dist/render/shaders/gpudriven/frameBlock.wgsl.d.ts +13 -0
  244. package/dist/render/shaders/gpudriven/frameBlock.wgsl.js +62 -0
  245. package/dist/render/shaders/gpudriven/materialBin.wgsl.d.ts +51 -0
  246. package/dist/render/shaders/gpudriven/materialBin.wgsl.js +112 -0
  247. package/dist/render/shaders/gpudriven/materialTable.wgsl.d.ts +25 -0
  248. package/dist/render/shaders/gpudriven/materialTable.wgsl.js +43 -0
  249. package/dist/render/shaders/gpudriven/meshTransform.wgsl.d.ts +18 -0
  250. package/dist/render/shaders/gpudriven/meshTransform.wgsl.js +37 -0
  251. package/dist/render/shaders/gpudriven/present.wgsl.d.ts +43 -0
  252. package/dist/render/shaders/gpudriven/present.wgsl.js +127 -0
  253. package/dist/render/shaders/gpudriven/shade.wgsl.d.ts +127 -0
  254. package/dist/render/shaders/gpudriven/shade.wgsl.js +874 -0
  255. package/dist/render/shaders/gpudriven/shadow.wgsl.d.ts +44 -0
  256. package/dist/render/shaders/gpudriven/shadow.wgsl.js +308 -0
  257. package/dist/render/shaders/gpudriven/visbufferRaster.wgsl.d.ts +28 -0
  258. package/dist/render/shaders/gpudriven/visbufferRaster.wgsl.js +245 -0
  259. package/dist/render/shaders/network.wgsl.d.ts +101 -0
  260. package/dist/render/shaders/network.wgsl.js +206 -0
  261. package/dist/render/shaders/recon/motion.wgsl.d.ts +44 -0
  262. package/dist/render/shaders/recon/motion.wgsl.js +96 -0
  263. package/dist/render/shaders/recon/resolve.wgsl.d.ts +51 -0
  264. package/dist/render/shaders/recon/resolve.wgsl.js +733 -0
  265. package/dist/render/temporalAa.js +8 -33
  266. package/dist/render/textLayout.js +12 -6
  267. package/dist/render/textRenderer.d.ts +3 -1
  268. package/dist/render/textRenderer.js +4 -1
  269. package/dist/render/translucentQueue.d.ts +6 -0
  270. package/dist/render/translucentQueue.js +11 -0
  271. package/dist/render/windStreakRenderer.d.ts +2 -1
  272. package/dist/render/windStreakRenderer.js +3 -1
  273. package/dist/world/cell.d.ts +63 -0
  274. package/dist/world/cell.js +181 -0
  275. package/dist/world/cellStream.d.ts +103 -0
  276. package/dist/world/cellStream.js +198 -0
  277. package/dist/world/freeze.d.ts +81 -0
  278. package/dist/world/freeze.js +114 -0
  279. package/dist/world/rebase.d.ts +29 -0
  280. package/dist/world/rebase.js +41 -0
  281. package/package.json +8 -3
  282. package/scripts/frames.mjs +92 -0
  283. package/scripts/gpuCompute.mjs +323 -0
  284. package/scripts/png.d.mts +16 -0
  285. package/scripts/png.mjs +25 -0
  286. package/src/core/loop.ts +19 -2
  287. package/src/core/storage.ts +5 -0
  288. package/src/geometry/meshBuilder.ts +5 -1
  289. package/src/index.ts +246 -4
  290. package/src/input/input.ts +6 -1
  291. package/src/math/exact.ts +69 -3
  292. package/src/math/frustum.ts +52 -3
  293. package/src/render/backend/api.ts +42 -1
  294. package/src/render/backend/budget.ts +7 -6
  295. package/src/render/backend/createRenderer.ts +92 -3
  296. package/src/render/backend/materialChanges.ts +54 -0
  297. package/src/render/backend/select.ts +8 -0
  298. package/src/render/backend/webgl2/renderer.ts +211 -9
  299. package/src/render/backend/webgpu/buffers.ts +182 -69
  300. package/src/render/backend/webgpu/fieldCompose.ts +482 -0
  301. package/src/render/backend/webgpu/giFieldPass.ts +200 -0
  302. package/src/render/backend/webgpu/gpuDrivenPass.ts +2993 -0
  303. package/src/render/backend/webgpu/linePass.ts +4 -39
  304. package/src/render/backend/webgpu/probeBake.ts +588 -0
  305. package/src/render/backend/webgpu/renderer.ts +1481 -76
  306. package/src/render/backend/webgpu/scatterPass.ts +15 -1
  307. package/src/render/backend/webgpu/surfaceTexturePass.ts +63 -36
  308. package/src/render/causticsRenderer.ts +5 -2
  309. package/src/render/depthConvention.ts +15 -0
  310. package/src/render/deviceFeatures.ts +77 -0
  311. package/src/render/fog.ts +185 -0
  312. package/src/render/frame/alias.ts +97 -0
  313. package/src/render/frame/deps.ts +155 -0
  314. package/src/render/frame/flushGraph.ts +135 -0
  315. package/src/render/frame/graphSchedule.ts +170 -0
  316. package/src/render/frame/index.ts +52 -0
  317. package/src/render/frame/lifetime.ts +61 -0
  318. package/src/render/frame/passTimings.ts +69 -0
  319. package/src/render/frame/validate.ts +40 -0
  320. package/src/render/frame/virtual.ts +91 -0
  321. package/src/render/gi/chain.ts +225 -0
  322. package/src/render/gi/denoise.ts +276 -0
  323. package/src/render/gi/fieldScene.ts +264 -0
  324. package/src/render/gi/globalField.ts +543 -0
  325. package/src/render/gi/probeGridFit.ts +89 -0
  326. package/src/render/gi/probeTrace.ts +143 -0
  327. package/src/render/gi/probeVolume.ts +288 -0
  328. package/src/render/gi/reflection.ts +163 -0
  329. package/src/render/gi/traceField.ts +223 -0
  330. package/src/render/gi/traceScreen.ts +106 -0
  331. package/src/render/gpuScene.ts +80 -0
  332. package/src/render/gpudriven/alphaTest.ts +33 -0
  333. package/src/render/gpudriven/clusterUpload.ts +123 -0
  334. package/src/render/gpudriven/clusterWorld.ts +239 -0
  335. package/src/render/gpudriven/compact.ts +80 -0
  336. package/src/render/gpudriven/cullClusters.ts +333 -0
  337. package/src/render/gpudriven/decodeTables.ts +332 -0
  338. package/src/render/gpudriven/frustum.ts +70 -0
  339. package/src/render/gpudriven/geometryHarness.ts +51 -0
  340. package/src/render/gpudriven/hzb.ts +113 -0
  341. package/src/render/gpudriven/ibl.ts +147 -0
  342. package/src/render/gpudriven/indirect.ts +89 -0
  343. package/src/render/gpudriven/instances.ts +234 -0
  344. package/src/render/gpudriven/lit.ts +329 -0
  345. package/src/render/gpudriven/lodCut.ts +62 -0
  346. package/src/render/gpudriven/materialBin.ts +131 -0
  347. package/src/render/gpudriven/materialTable.ts +164 -0
  348. package/src/render/gpudriven/pipeline.ts +336 -0
  349. package/src/render/gpudriven/rangeAlloc.ts +122 -0
  350. package/src/render/gpudriven/sceneUpload.ts +173 -0
  351. package/src/render/gpudriven/shadeBins.ts +222 -0
  352. package/src/render/gpudriven/shadow.ts +285 -0
  353. package/src/render/gpudriven/shadowCamera.ts +406 -0
  354. package/src/render/gpudriven/streamScene.ts +575 -0
  355. package/src/render/gpudriven/surfaceFrame.ts +68 -0
  356. package/src/render/gpudriven/twoPhase.ts +214 -0
  357. package/src/render/gpudriven/visbuffer.ts +41 -0
  358. package/src/render/halfFloat.ts +22 -4
  359. package/src/render/inference/attentionTiled.ts +132 -0
  360. package/src/render/inference/denseKernels.ts +369 -0
  361. package/src/render/inference/device.ts +44 -0
  362. package/src/render/inference/deviceGraph.ts +39 -0
  363. package/src/render/inference/fuse.ts +47 -0
  364. package/src/render/inference/kernelKit.ts +120 -0
  365. package/src/render/inference/kernels.ts +18 -0
  366. package/src/render/inference/plan.ts +26 -0
  367. package/src/render/inference/reuse.ts +79 -0
  368. package/src/render/inference/runner.ts +204 -0
  369. package/src/render/inference/schedule.ts +80 -0
  370. package/src/render/inference/shapeKernels.ts +112 -0
  371. package/src/render/inference/spatialKernels.ts +220 -0
  372. package/src/render/inference/splatRaster.wgsl.ts +348 -0
  373. package/src/render/inference/splatTiles.ts +186 -0
  374. package/src/render/lineBatch.ts +4 -24
  375. package/src/render/mesh.ts +46 -1
  376. package/src/render/pass.ts +155 -10
  377. package/src/render/prefilterEnvMap.ts +33 -0
  378. package/src/render/readback.ts +73 -0
  379. package/src/render/recon/clamp.ts +113 -0
  380. package/src/render/recon/disocclusion.ts +143 -0
  381. package/src/render/recon/frameGen.ts +191 -0
  382. package/src/render/recon/frameSizes.ts +64 -0
  383. package/src/render/recon/jitter.ts +126 -0
  384. package/src/render/recon/motionVectors.ts +138 -0
  385. package/src/render/recon/reproject.ts +179 -0
  386. package/src/render/recon/resolve.ts +444 -0
  387. package/src/render/recon/tier.ts +31 -0
  388. package/src/render/reflectionProbe.ts +34 -0
  389. package/src/render/renderQuality.ts +87 -0
  390. package/src/render/screenSpaceReflection.ts +37 -3
  391. package/src/render/sdfFont.ts +2 -0
  392. package/src/render/segmentQuads.ts +39 -0
  393. package/src/render/shaders/flat/lobes.ts +35 -3
  394. package/src/render/shaders/flat/preamble.ts +25 -1
  395. package/src/render/shaders/flat/probeGrid.ts +77 -2
  396. package/src/render/shaders/generated/flat.wgsl.ts +64 -24
  397. package/src/render/shaders/gi/composeField.wgsl.ts +239 -0
  398. package/src/render/shaders/gi/marchField.wgsl.ts +146 -0
  399. package/src/render/shaders/gi/probeBake.wgsl.ts +649 -0
  400. package/src/render/shaders/gi/probeTrace.wgsl.ts +269 -0
  401. package/src/render/shaders/gi/probeVolume.wgsl.ts +246 -0
  402. package/src/render/shaders/gi/sampleField.wgsl.ts +140 -0
  403. package/src/render/shaders/gpudriven/blendRaster.wgsl.ts +343 -0
  404. package/src/render/shaders/gpudriven/compact.wgsl.ts +59 -0
  405. package/src/render/shaders/gpudriven/cull.wgsl.ts +433 -0
  406. package/src/render/shaders/gpudriven/decode.wgsl.ts +373 -0
  407. package/src/render/shaders/gpudriven/environment.wgsl.ts +141 -0
  408. package/src/render/shaders/gpudriven/fog.wgsl.ts +110 -0
  409. package/src/render/shaders/gpudriven/frameBlock.wgsl.ts +62 -0
  410. package/src/render/shaders/gpudriven/materialBin.wgsl.ts +116 -0
  411. package/src/render/shaders/gpudriven/materialTable.wgsl.ts +46 -0
  412. package/src/render/shaders/gpudriven/meshTransform.wgsl.ts +37 -0
  413. package/src/render/shaders/gpudriven/present.wgsl.ts +129 -0
  414. package/src/render/shaders/gpudriven/shade.wgsl.ts +884 -0
  415. package/src/render/shaders/gpudriven/shadow.wgsl.ts +322 -0
  416. package/src/render/shaders/gpudriven/visbufferRaster.wgsl.ts +251 -0
  417. package/src/render/shaders/network.wgsl.ts +238 -0
  418. package/src/render/shaders/recon/motion.wgsl.ts +101 -0
  419. package/src/render/shaders/recon/resolve.wgsl.ts +741 -0
  420. package/src/render/temporalAa.ts +9 -33
  421. package/src/render/textLayout.ts +12 -6
  422. package/src/render/textRenderer.ts +5 -2
  423. package/src/render/translucentQueue.ts +11 -0
  424. package/src/render/windStreakRenderer.ts +4 -2
  425. package/src/world/cell.ts +231 -0
  426. package/src/world/cellStream.ts +278 -0
  427. package/src/world/freeze.ts +157 -0
  428. package/src/world/rebase.ts +56 -0
@@ -0,0 +1,147 @@
1
+ /** The split sum's second half, in TypeScript, so the second pipeline's copy has a reference. */
2
+
3
+ /**
4
+ * **`flat/lobes.ts` in TypeScript: how much of an environment a surface returns.**
5
+ *
6
+ * The convolution decides *what* a surface reflects — a rougher one reads a blurrier level of the
7
+ * prefiltered chain — and this decides *how much*. Both halves are a fit and an algebraic
8
+ * compensation, which is to say both are arithmetic nobody can check by looking at a frame: the
9
+ * failure they exist to prevent was reported twice from consumers as *metals reading opaque*, and
10
+ * traced to a term that asserts a perfect mirror absorbs 55% of the light once it is roughened.
11
+ *
12
+ * **Metalness is a per-material scalar here, not an ORM map.** `flat/main.ts` reads it per texel
13
+ * from the blue channel of an occlusion-roughness-metallic image, which is a thing this pipeline
14
+ * has no textures to read; a GPU-driven material carries one number instead. That is a *subset* and
15
+ * not a second opinion: everywhere a texel could vary, a material is constant, and every expression
16
+ * below is the forward path's with a uniform standing in for a fetch.
17
+ *
18
+ * **What stays collapsed is the environment gain.** `flat/main.ts` scales what a metal reflects by
19
+ * `uEnvironmentGain`, which exists because a metal has no diffuse behind its reflection and a probe
20
+ * that under-reports the room leaves it with no other way to be lit. `PassEnvironment` carries no
21
+ * such control, so the gain is 1 and `mix(1, gain, metal)` is exactly 1 at every metalness — a
22
+ * collapse that is exact rather than approximate, which is the standard every term here is held to.
23
+ */
24
+
25
+ /** A dielectric's normal-incidence reflectance, which is what a material with no metalness is. */
26
+ export const DIELECTRIC_F0 = 0.04;
27
+
28
+ /**
29
+ * A surface's normal-incidence reflectance, from its metalness.
30
+ *
31
+ * **A metal's Fresnel base is close to its albedo and a dielectric's is four per cent**, and
32
+ * `flat/main.ts` records what leaving it at 0.04 costs: a smooth metal seen head-on takes a four
33
+ * per cent reflection and reads as dark paint. The chromatic half of that — a metal's `f0` being
34
+ * its own colour rather than white — is in `lit.ts`, where the albedo is; this is the scalar the
35
+ * split sum takes.
36
+ */
37
+ export function surfaceF0(metalness: number): number {
38
+ return DIELECTRIC_F0 + (1 - DIELECTRIC_F0) * metalness;
39
+ }
40
+
41
+ /**
42
+ * The split sum's second half, as a two-term fit: `dfg.x` scales `f0` and `dfg.y` is added on top.
43
+ *
44
+ * **Karis's analytic fit rather than the tabulated integral**, which is a texture this pipeline
45
+ * would otherwise have to carry and bind. Its published error is around one per cent of the
46
+ * tabulated term, far below what an eight-bit frame can show. **What would make it wrong** is a
47
+ * material model with a second lobe — a clearcoat or a sheen carries its own BRDF and its own
48
+ * integral, and this is Trowbridge-Reitz with Smith masking and nothing else.
49
+ */
50
+ export function envBrdfApprox(ndv: number, roughness: number, out: Float32Array): Float32Array {
51
+ const c0 = [-1, -0.0275, -0.572, 0.022];
52
+ const c1 = [1, 0.0425, 1.04, -0.04];
53
+ const rx = roughness * (c0[0] as number) + (c1[0] as number);
54
+ const ry = roughness * (c0[1] as number) + (c1[1] as number);
55
+ const rz = roughness * (c0[2] as number) + (c1[2] as number);
56
+ const rw = roughness * (c0[3] as number) + (c1[3] as number);
57
+ const a004 = Math.min(rx * rx, Math.pow(2, -9.28 * ndv)) * rx + ry;
58
+ out[0] = -1.04 * a004 + rz;
59
+ out[1] = 1.04 * a004 + rw;
60
+ return out;
61
+ }
62
+
63
+ /**
64
+ * The same, with the light that bounces more than once inside the microsurface put back.
65
+ *
66
+ * **The split sum integrates a *single* scattering event**, so it silently discards every ray that
67
+ * strikes one microfacet, bounces, and leaves. Roughness scatters light; it does not absorb it, so
68
+ * a surface with `f0` 1 has to return everything at every roughness — and the bare sum returns
69
+ * about `1 - 0.55 * roughness`, which is a 44% loss at roughness 0.8.
70
+ *
71
+ * Fdez-Agüera's compensation: the missing energy, returned at the surface's average Fresnel over
72
+ * the hemisphere, summed as a geometric series over repeated bounces. **At `f0` 1 it comes back to
73
+ * exactly 1.0 at every roughness** — `average` is 1, `multi` is `energy / energy`, and the sum
74
+ * telescopes — which is the invariant the test beside this file asserts across the whole range.
75
+ *
76
+ * **No divide guard, because the denominator cannot reach zero.** `missing` is at most 0.55 and
77
+ * `average` at most 1, so `1 - missing * average` bottoms out at 0.45 — the worst case in both
78
+ * variables at once.
79
+ */
80
+ export function envSpecularEnergy(f0: number, dfgX: number, dfgY: number): number {
81
+ const single = f0 * dfgX + dfgY;
82
+ const energy = dfgX + dfgY;
83
+ const missing = 1 - energy;
84
+ const average = f0 + (1 - f0) / 21;
85
+ const multi = (single * average) / (1 - missing * average);
86
+ return single + multi * missing;
87
+ }
88
+
89
+ /**
90
+ * How much of what the surface sees in the mirror direction reaches the frame.
91
+ *
92
+ * **Two expressions and a selector, because they answer different questions.** `prefiltered` is 1
93
+ * where the radiance came from a GGX-convolved chain, and there the split sum is the correct share
94
+ * of that integral — it carries the angular dependence itself, so it replaces the Fresnel sweep
95
+ * rather than multiplying it. Where the radiance is anything else, applying it would assert an
96
+ * integral that was never performed: `flat/main.ts` measured that mistake as the grazing-to-head-on
97
+ * ratio falling from about 15 to 1 down to 5 to 1, reported twice as a metal going opaque.
98
+ *
99
+ * So without a chain this is the engine's own long-standing weight — Schlick's Fresnel times
100
+ * `reflectivity * (1 - roughness)` — and with one it is the integral. The two are deliberately not
101
+ * blended into a single formula; a selector that is exactly 0 or exactly 1 is what makes a scene
102
+ * with no environment arithmetically identical to the frame it already rendered.
103
+ *
104
+ * `ndv` is `dot(n, toEye)` clamped at zero, as the caller's own facing term already computes.
105
+ */
106
+ export function environmentWeight(
107
+ ndv: number,
108
+ roughness: number,
109
+ reflectivity: number,
110
+ prefiltered: number,
111
+ /**
112
+ * 0 is a dielectric and 1 is a metal. **Defaults to 0**, so every caller written before this
113
+ * argument existed gets the arithmetic it had.
114
+ */
115
+ metalness = 0,
116
+ ): number {
117
+ const f0 = surfaceF0(metalness);
118
+ const facing = 1 - ndv;
119
+ const fresnel = f0 + (1 - f0) * (facing * facing * facing * facing * facing);
120
+ /*
121
+ * **`max(reflectivity * (1 - roughness), metal)`, and both halves of that are repairs.**
122
+ *
123
+ * `reflectivity` is a per-material control, and it is zero for exactly the surfaces that carry
124
+ * their metalness some other way — `flat/main.ts` measured reading it alone as a chromed subject
125
+ * coming out near black with a baked probe sitting unread, because the whole block was skipped.
126
+ * So a metal reflects whether or not the material asked for reflections.
127
+ *
128
+ * And the `1 - roughness` thinning does not reach the metal. It stands for a polish that
129
+ * scatters a dielectric's reflection into no image at all; a metal has no diffuse to fall back
130
+ * on, so scaling it throws away 40% of everything a surface at roughness 0.4 shows and replaces
131
+ * it with a dark constant. It is also double counting — the level the sample comes from already
132
+ * *is* the roughness.
133
+ */
134
+ const amount = Math.max(reflectivity * (1 - roughness), metalness);
135
+ const sweep = Math.min(Math.max(fresnel * amount, 0), 1);
136
+
137
+ const dfg = new Float32Array(2);
138
+ envBrdfApprox(ndv, roughness, dfg);
139
+ const integrated = Math.min(
140
+ Math.max(
141
+ envSpecularEnergy(f0, dfg[0] as number, dfg[1] as number) * Math.max(reflectivity, metalness),
142
+ 0,
143
+ ),
144
+ 1,
145
+ );
146
+ return sweep + (integrated - sweep) * prefiltered;
147
+ }
@@ -0,0 +1,89 @@
1
+ /**
2
+ * The arguments a culled frame hands the rasteriser, written by the device and read by it.
3
+ *
4
+ * **The whole pipeline exists so that the processor never learns how much there is to draw.** A
5
+ * cull dispatch writes the count into a buffer and the draw reads it from the same buffer; the
6
+ * frame is submitted before either number exists. So these are layouts rather than calls: what a
7
+ * block holds, in what order, and what a caller must have cleared before the dispatch runs.
8
+ *
9
+ * **One draw for the whole frame, not one per cluster.** Every surviving cluster is an *instance*
10
+ * of a fixed 128-triangle draw, which is what `VIS_MAX_TRIANGLES` is for: the index count is the
11
+ * same for all of them, so there is one block instead of thousands and no multi-draw extension is
12
+ * needed. A cluster with fewer triangles emits degenerate ones for the remainder — see
13
+ * `clusterVertexCluster` — and a degenerate triangle covers no pixel, so the picture is unchanged
14
+ * and the cost is the vertex stage running on indices that produce nothing.
15
+ *
16
+ * **What that costs, stated rather than hidden**: a scene whose clusters average 64 triangles runs
17
+ * the vertex stage twice for every triangle it draws. The alternative is a block per cluster and a
18
+ * loop of indirect draws on the processor, which is the draw-call bound this pipeline exists to
19
+ * remove. If a consumer's clusters are persistently half full, the fix is in the *baker* — see
20
+ * `assets/src/bake/cluster.ts`, whose target is the full 128.
21
+ */
22
+
23
+ import { VIS_MAX_TRIANGLES } from './visbuffer.ts';
24
+
25
+ /** Words in a `drawIndexedIndirect` block: index count, instances, first index, base vertex, first instance. */
26
+ export const INDIRECT_DRAW_WORDS = 5;
27
+
28
+ /** Words in a `dispatchWorkgroups` block: three group counts. */
29
+ export const INDIRECT_DISPATCH_WORDS = 3;
30
+
31
+ /** Indices a cluster draw always issues, whatever the cluster actually holds. */
32
+ export const CLUSTER_INDEX_CAP = VIS_MAX_TRIANGLES * 3;
33
+
34
+ /**
35
+ * The draw block for a frame that kept `clusters` of them.
36
+ *
37
+ * `firstInstance` is zero and the *instance index* is the slot in the kept list rather than the
38
+ * cluster identifier: the list is compacted, so slot and identifier differ, and the shader reads
39
+ * the identifier out of the list. Putting the identifier here instead would need a block per
40
+ * cluster, which is the thing this avoids.
41
+ */
42
+ export function writeClusterDrawArgs(out: Uint32Array, at: number, clusters: number): void {
43
+ out[at] = CLUSTER_INDEX_CAP;
44
+ out[at + 1] = Math.max(0, clusters);
45
+ out[at + 2] = 0;
46
+ out[at + 3] = 0;
47
+ out[at + 4] = 0;
48
+ }
49
+
50
+ /**
51
+ * The dispatch block for `count` items at `groupSize` a group.
52
+ *
53
+ * **Rounded up, and zero items is zero groups rather than one.** A group that runs with nothing to
54
+ * do is harmless in this pipeline — every entry point range-checks its own invocation — but a
55
+ * dispatch of one group over an empty list still reads a buffer that a caller may not have bound,
56
+ * and "nothing to do" is the case a frame hits before anything has streamed in.
57
+ *
58
+ * **Zero falls out of the division rather than out of a special case**, and the special case that
59
+ * stood here is gone because a perturbation survived it: `ceil(0 / n)` is already 0, so the branch
60
+ * changed no answer and was a second statement of the rule to keep in step. The behaviour is still
61
+ * held by a test — what is gone is the code that was not doing the holding.
62
+ */
63
+ export function writeDispatchArgs(
64
+ out: Uint32Array,
65
+ at: number,
66
+ count: number,
67
+ groupSize: number,
68
+ ): void {
69
+ const groups = Math.ceil(Math.max(0, count) / Math.max(1, groupSize));
70
+ out[at] = groups;
71
+ out[at + 1] = 1;
72
+ out[at + 2] = 1;
73
+ }
74
+
75
+ /**
76
+ * Which slot of the kept list a vertex belongs to, and which triangle of that cluster.
77
+ *
78
+ * The vertex stage's whole addressing, on the processor so a test can read it. `vertex` is
79
+ * `@builtin(vertex_index)` and runs to `CLUSTER_INDEX_CAP`; `instance` is
80
+ * `@builtin(instance_index)` and is the slot.
81
+ *
82
+ * **A triangle past the cluster's own count is reported rather than clamped.** Clamping draws the
83
+ * last triangle of the cluster over and over, which is a visible sliver on every cluster that is
84
+ * not exactly full; reporting it lets the shader emit a degenerate triangle, which covers nothing.
85
+ */
86
+ export function clusterVertexCluster(vertex: number, instance: number, triangleCount: number) {
87
+ const triangle = Math.floor(vertex / 3);
88
+ return { slot: instance, triangle, corner: vertex % 3, degenerate: triangle >= triangleCount };
89
+ }
@@ -0,0 +1,234 @@
1
+ /**
2
+ * Whole meshes out of view, dropped before anything reads their clusters.
3
+ *
4
+ * **An instance here is a mesh**: every cluster names one through `meshOf`, and the raster and the
5
+ * shading both place it by `transforms[mesh]`. A mesh wholly outside the view has nothing to
6
+ * contribute to the frame, and before this every one of its clusters was still cut, tested against
7
+ * six planes and a cone, and occlusion-tested in phase two — correct, and the cost this stage
8
+ * removes.
9
+ *
10
+ * **The flag is read by the cut**, `LOD_CUT_WGSL`, because that is the one per-cluster stage with
11
+ * room for it: the cluster cull binds seven storage buffers and the compaction seven, of the eight a
12
+ * stage may bind by default, and the lookup needs two more (the flags and the cluster's mesh). A
13
+ * cluster the cut does not select is one the cluster cull then skips without testing and the
14
+ * compaction never lists — so a hidden mesh costs a lookup per cluster, and so does every cluster
15
+ * at a level the cut did not choose, which used to be tested for nothing.
16
+ *
17
+ * **The spheres are fitted once, at upload**, from the clusters' world spheres `clusterWorld.ts`
18
+ * already produced, for the reason that file gives: this pipeline's geometry does not move.
19
+ */
20
+ import { CLUSTER_CULL_FLOATS } from './cullClusters.ts';
21
+ import { sphereOutsideFrustum } from './frustum.ts';
22
+ import { clusterSelected } from './lodCut.ts';
23
+
24
+ /** Four floats a mesh: world centre and radius. `CULL_INSTANCES_WGSL` reads this stride. */
25
+ export const INSTANCE_FLOATS = 4;
26
+
27
+ /** What the instance cull writes for a mesh wholly outside the view. Zero is everything else. */
28
+ export const INSTANCE_HIDDEN = 1;
29
+
30
+ /** Four words a cluster in the uploaded meta: index offset, index count, identifier, mesh. */
31
+ export const CLUSTER_META_WORDS = 4;
32
+ const META_MESH = 3;
33
+
34
+ /** Six floats a cluster in the cut's input, as `clusterWorld.ts` lays out `lod`. */
35
+ const LOD_FLOATS = 6;
36
+
37
+ /**
38
+ * Each mesh's world bounding sphere, from the world spheres of its clusters.
39
+ *
40
+ * **Held to the numbers the device reads.** The centre is the middle of the clusters' box, stored
41
+ * in single precision first; the radius is then measured from the *stored* centre and rounded up,
42
+ * never to nearest — a sphere rounded the wrong way is a few parts in ten million short of a
43
+ * cluster's far edge, and a cull that is not conservative is a hole in the picture.
44
+ *
45
+ * A mesh no cluster names is a point at the origin, which draws nothing whatever the cull says. A
46
+ * cluster naming a mesh past `meshCount` is refused: it would be bounded by nothing and drawn by a
47
+ * transform that does not exist.
48
+ */
49
+ export function instanceBounds(
50
+ cull: Float32Array,
51
+ meshOf: Uint32Array,
52
+ count: number,
53
+ meshCount: number,
54
+ ): Float32Array {
55
+ const low = new Float64Array(meshCount * 3).fill(Infinity);
56
+ const high = new Float64Array(meshCount * 3).fill(-Infinity);
57
+ for (let c = 0; c < count; c += 1) {
58
+ const mesh = meshOf[c] as number;
59
+ if (!(mesh < meshCount)) {
60
+ throw new RangeError(
61
+ `instanceBounds: cluster ${String(c)} names mesh ${String(mesh)} and the scene has ${String(meshCount)} meshes`,
62
+ );
63
+ }
64
+ const at = c * CLUSTER_CULL_FLOATS;
65
+ const radius = cull[at + 3] as number;
66
+ for (let axis = 0; axis < 3; axis += 1) {
67
+ const centre = cull[at + axis] as number;
68
+ low[mesh * 3 + axis] = Math.min(low[mesh * 3 + axis] as number, centre - radius);
69
+ high[mesh * 3 + axis] = Math.max(high[mesh * 3 + axis] as number, centre + radius);
70
+ }
71
+ }
72
+
73
+ const out = new Float32Array(meshCount * INSTANCE_FLOATS);
74
+ for (let mesh = 0; mesh < meshCount; mesh += 1) {
75
+ if (!((low[mesh * 3] as number) <= (high[mesh * 3] as number))) continue;
76
+ for (let axis = 0; axis < 3; axis += 1) {
77
+ out[mesh * INSTANCE_FLOATS + axis] =
78
+ ((low[mesh * 3 + axis] as number) + (high[mesh * 3 + axis] as number)) / 2;
79
+ }
80
+ }
81
+
82
+ const reach = new Float64Array(meshCount);
83
+ for (let c = 0; c < count; c += 1) {
84
+ const mesh = meshOf[c] as number;
85
+ const at = c * CLUSTER_CULL_FLOATS;
86
+ const from = mesh * INSTANCE_FLOATS;
87
+ const distance = Math.hypot(
88
+ (cull[at] as number) - (out[from] as number),
89
+ (cull[at + 1] as number) - (out[from + 1] as number),
90
+ (cull[at + 2] as number) - (out[from + 2] as number),
91
+ );
92
+ reach[mesh] = Math.max(reach[mesh] as number, distance + (cull[at + 3] as number));
93
+ }
94
+ for (let mesh = 0; mesh < meshCount; mesh += 1) {
95
+ const wanted = reach[mesh] as number;
96
+ let radius = Math.fround(wanted);
97
+ /* Up, not to nearest: one step past the rounding is the smallest sphere that still holds. */
98
+ if (radius < wanted) radius = Math.fround(wanted * (1 + 2 ** -23));
99
+ out[mesh * INSTANCE_FLOATS + 3] = radius;
100
+ }
101
+ return out;
102
+ }
103
+
104
+ /**
105
+ * One mesh's bounding sphere, from a contiguous run of its clusters' world bounds.
106
+ *
107
+ * **A run rather than a `meshOf` scan, because a streamed mesh's clusters are contiguous by
108
+ * construction** — the allocator hands out a range — so the scan `instanceBounds` does is work a
109
+ * streaming caller has already done.
110
+ *
111
+ * **A mesh with no clusters gets a sphere of zero radius rather than an empty box's arithmetic.**
112
+ * `Infinity - (-Infinity)` is `-Infinity`, and every comparison against `NaN` is false, which
113
+ * reads as a mesh that is never culled rather than one that is never drawn.
114
+ *
115
+ * The two forms must agree, and `instances.test.ts` is what holds them to it.
116
+ */
117
+ export function instanceSphereInto(
118
+ cull: Float32Array,
119
+ from: number,
120
+ count: number,
121
+ out: Float32Array,
122
+ at: number,
123
+ ): void {
124
+ const base = at * INSTANCE_FLOATS;
125
+ if (count <= 0) {
126
+ out[base] = 0;
127
+ out[base + 1] = 0;
128
+ out[base + 2] = 0;
129
+ out[base + 3] = 0;
130
+ return;
131
+ }
132
+
133
+ const low = [Infinity, Infinity, Infinity];
134
+ const high = [-Infinity, -Infinity, -Infinity];
135
+ for (let c = from; c < from + count; c += 1) {
136
+ const record = c * CLUSTER_CULL_FLOATS;
137
+ const radius = cull[record + 3] as number;
138
+ for (let axis = 0; axis < 3; axis += 1) {
139
+ const centre = cull[record + axis] as number;
140
+ low[axis] = Math.min(low[axis] as number, centre - radius);
141
+ high[axis] = Math.max(high[axis] as number, centre + radius);
142
+ }
143
+ }
144
+ for (let axis = 0; axis < 3; axis += 1) {
145
+ out[base + axis] = ((low[axis] as number) + (high[axis] as number)) / 2;
146
+ }
147
+
148
+ /* The reach from that centre, which is what makes the sphere hold every cluster rather than
149
+ every corner of the box — the same second pass `instanceBounds` makes, for the same reason. */
150
+ let reach = 0;
151
+ for (let c = from; c < from + count; c += 1) {
152
+ const record = c * CLUSTER_CULL_FLOATS;
153
+ const distance = Math.hypot(
154
+ (cull[record] as number) - (out[base] as number),
155
+ (cull[record + 1] as number) - (out[base + 1] as number),
156
+ (cull[record + 2] as number) - (out[base + 2] as number),
157
+ );
158
+ reach = Math.max(reach, distance + (cull[record + 3] as number));
159
+ }
160
+ let radius = Math.fround(reach);
161
+ /* Up, not to nearest: one step past the rounding is the smallest sphere that still holds. */
162
+ if (radius < reach) radius = Math.fround(reach * (1 + 2 ** -23));
163
+ out[base + 3] = radius;
164
+ }
165
+
166
+ /**
167
+ * One flag a mesh: `INSTANCE_HIDDEN` where its sphere is wholly outside the view. Returns how many
168
+ * are. Mirrors `CULL_INSTANCES_WGSL`, which `gpu-parity.mjs` holds to `sphereOutsideFrustum`.
169
+ */
170
+ export function cullInstances(
171
+ planes: Float32Array,
172
+ spheres: Float32Array,
173
+ count: number,
174
+ out: Uint32Array,
175
+ ): number {
176
+ let hidden = 0;
177
+ for (let i = 0; i < count; i += 1) {
178
+ const at = i * INSTANCE_FLOATS;
179
+ const outside = sphereOutsideFrustum(
180
+ planes,
181
+ spheres[at] as number,
182
+ spheres[at + 1] as number,
183
+ spheres[at + 2] as number,
184
+ spheres[at + 3] as number,
185
+ );
186
+ out[i] = outside ? INSTANCE_HIDDEN : 0;
187
+ if (outside) hidden += 1;
188
+ }
189
+ return hidden;
190
+ }
191
+
192
+ /**
193
+ * The cut, over every cluster, with the instance flags applied first: one flag a cluster, and how
194
+ * many were selected. Mirrors `LOD_CUT_WGSL`.
195
+ */
196
+ export function selectClusters(
197
+ lod: Float32Array,
198
+ meta: Uint32Array,
199
+ hidden: Uint32Array,
200
+ screenHeight: number,
201
+ fovY: number,
202
+ thresholdPixels: number,
203
+ eyeX: number,
204
+ eyeY: number,
205
+ eyeZ: number,
206
+ out: Uint32Array,
207
+ ): number {
208
+ const count = Math.floor(lod.length / LOD_FLOATS);
209
+ let chosen = 0;
210
+ for (let i = 0; i < count; i += 1) {
211
+ const mesh = meta[i * CLUSTER_META_WORDS + META_MESH] as number;
212
+ if ((hidden[mesh] ?? 0) !== 0) {
213
+ out[i] = 0;
214
+ continue;
215
+ }
216
+ const at = i * LOD_FLOATS;
217
+ const selected = clusterSelected(
218
+ lod[at + 4] as number,
219
+ lod[at + 5] as number,
220
+ Math.hypot(
221
+ (lod[at] as number) - eyeX,
222
+ (lod[at + 1] as number) - eyeY,
223
+ (lod[at + 2] as number) - eyeZ,
224
+ ),
225
+ lod[at + 3] as number,
226
+ screenHeight,
227
+ fovY,
228
+ thresholdPixels,
229
+ );
230
+ out[i] = selected ? 1 : 0;
231
+ if (selected) chosen += 1;
232
+ }
233
+ return chosen;
234
+ }