@thunderclap-engine/engine 0.0.0-stage → 1.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 (566) hide show
  1. package/CHANGELOG.md +390 -0
  2. package/LICENSE +21 -0
  3. package/README.md +333 -2
  4. package/dist/analytics/analytics.d.ts +80 -0
  5. package/dist/analytics/analytics.js +129 -0
  6. package/dist/analytics/index.d.ts +1 -0
  7. package/dist/analytics/index.js +1 -0
  8. package/dist/anim/aim.d.ts +43 -0
  9. package/dist/anim/aim.js +157 -0
  10. package/dist/anim/clip.d.ts +98 -0
  11. package/dist/anim/clip.js +282 -0
  12. package/dist/anim/compress.d.ts +65 -0
  13. package/dist/anim/compress.js +339 -0
  14. package/dist/anim/foot-ik.d.ts +74 -0
  15. package/dist/anim/foot-ik.js +241 -0
  16. package/dist/anim/graph.d.ts +153 -0
  17. package/dist/anim/graph.js +532 -0
  18. package/dist/anim/ik.d.ts +105 -0
  19. package/dist/anim/ik.js +435 -0
  20. package/dist/anim/index.d.ts +36 -0
  21. package/dist/anim/index.js +21 -0
  22. package/dist/anim/match.d.ts +66 -0
  23. package/dist/anim/match.js +153 -0
  24. package/dist/anim/mixer.d.ts +113 -0
  25. package/dist/anim/mixer.js +314 -0
  26. package/dist/anim/morph.d.ts +41 -0
  27. package/dist/anim/morph.js +94 -0
  28. package/dist/anim/pose.d.ts +74 -0
  29. package/dist/anim/pose.js +254 -0
  30. package/dist/anim/quat.d.ts +44 -0
  31. package/dist/anim/quat.js +187 -0
  32. package/dist/anim/retarget.d.ts +97 -0
  33. package/dist/anim/retarget.js +366 -0
  34. package/dist/anim/root-motion.d.ts +44 -0
  35. package/dist/anim/root-motion.js +224 -0
  36. package/dist/anim/skin-pose.d.ts +39 -0
  37. package/dist/anim/skin-pose.js +53 -0
  38. package/dist/anim/socket.d.ts +47 -0
  39. package/dist/anim/socket.js +84 -0
  40. package/dist/anim/spring-chain.d.ts +73 -0
  41. package/dist/anim/spring-chain.js +294 -0
  42. package/dist/assets/atlas.d.ts +90 -0
  43. package/dist/assets/atlas.js +212 -0
  44. package/dist/assets/baked.d.ts +33 -0
  45. package/dist/assets/baked.js +38 -0
  46. package/dist/assets/bundle.d.ts +88 -0
  47. package/dist/assets/bundle.js +464 -0
  48. package/dist/assets/cache.d.ts +56 -0
  49. package/dist/assets/cache.js +123 -0
  50. package/dist/assets/gltf-load.d.ts +252 -0
  51. package/dist/assets/gltf-load.js +661 -0
  52. package/dist/assets/gltf-skin.d.ts +36 -0
  53. package/dist/assets/gltf-skin.js +109 -0
  54. package/dist/assets/gltf.d.ts +706 -0
  55. package/dist/assets/gltf.js +1376 -0
  56. package/dist/assets/index.d.ts +20 -0
  57. package/dist/assets/index.js +10 -0
  58. package/dist/assets/ktx2.d.ts +71 -0
  59. package/dist/assets/ktx2.js +213 -0
  60. package/dist/assets/manifest.d.ts +79 -0
  61. package/dist/assets/manifest.js +7 -0
  62. package/dist/assets/meshopt.d.ts +23 -0
  63. package/dist/assets/meshopt.js +403 -0
  64. package/dist/audio/audio.d.ts +733 -0
  65. package/dist/audio/audio.js +2013 -0
  66. package/dist/audio/dsp.d.ts +61 -0
  67. package/dist/audio/dsp.js +159 -0
  68. package/dist/audio/duck.d.ts +44 -0
  69. package/dist/audio/duck.js +101 -0
  70. package/dist/audio/envelope.d.ts +53 -0
  71. package/dist/audio/envelope.js +106 -0
  72. package/dist/audio/index.d.ts +13 -0
  73. package/dist/audio/index.js +14 -0
  74. package/dist/audio/music.d.ts +113 -0
  75. package/dist/audio/music.js +548 -0
  76. package/dist/audio/occlusion.d.ts +55 -0
  77. package/dist/audio/occlusion.js +61 -0
  78. package/dist/audio/persist.d.ts +23 -0
  79. package/dist/audio/persist.js +27 -0
  80. package/dist/audio/recipes.d.ts +55 -0
  81. package/dist/audio/recipes.js +227 -0
  82. package/dist/audio/send.d.ts +41 -0
  83. package/dist/audio/send.js +118 -0
  84. package/dist/audio/sprites.d.ts +59 -0
  85. package/dist/audio/sprites.js +43 -0
  86. package/dist/audio/synth.d.ts +103 -0
  87. package/dist/audio/synth.js +197 -0
  88. package/dist/audio/variations.d.ts +25 -0
  89. package/dist/audio/variations.js +55 -0
  90. package/dist/audio/zones.d.ts +100 -0
  91. package/dist/audio/zones.js +167 -0
  92. package/dist/core/detached-surface.d.ts +44 -0
  93. package/dist/core/detached-surface.js +104 -0
  94. package/dist/core/events.d.ts +24 -0
  95. package/dist/core/events.js +43 -0
  96. package/dist/core/game.d.ts +409 -0
  97. package/dist/core/game.js +530 -0
  98. package/dist/core/hash.d.ts +24 -0
  99. package/dist/core/hash.js +49 -0
  100. package/dist/core/index.d.ts +17 -0
  101. package/dist/core/index.js +9 -0
  102. package/dist/core/loop.d.ts +113 -0
  103. package/dist/core/loop.js +185 -0
  104. package/dist/core/math.d.ts +58 -0
  105. package/dist/core/math.js +210 -0
  106. package/dist/core/noise.d.ts +40 -0
  107. package/dist/core/noise.js +247 -0
  108. package/dist/core/pool.d.ts +42 -0
  109. package/dist/core/pool.js +85 -0
  110. package/dist/core/random.d.ts +61 -0
  111. package/dist/core/random.js +199 -0
  112. package/dist/core/scene.d.ts +79 -0
  113. package/dist/core/scene.js +195 -0
  114. package/dist/core/timer.d.ts +66 -0
  115. package/dist/core/timer.js +286 -0
  116. package/dist/fx/billboard-wgsl.d.ts +29 -0
  117. package/dist/fx/billboard-wgsl.js +158 -0
  118. package/dist/fx/billboard.d.ts +249 -0
  119. package/dist/fx/billboard.js +702 -0
  120. package/dist/fx/debug-draw.d.ts +127 -0
  121. package/dist/fx/debug-draw.js +523 -0
  122. package/dist/fx/debug-wgsl.d.ts +7 -0
  123. package/dist/fx/debug-wgsl.js +32 -0
  124. package/dist/fx/decals-wgsl.d.ts +15 -0
  125. package/dist/fx/decals-wgsl.js +173 -0
  126. package/dist/fx/decals.d.ts +216 -0
  127. package/dist/fx/decals.js +607 -0
  128. package/dist/fx/effect.d.ts +129 -0
  129. package/dist/fx/effect.js +349 -0
  130. package/dist/fx/effects/hit-spark.json +48 -0
  131. package/dist/fx/effects/index.d.ts +8 -0
  132. package/dist/fx/effects/index.js +9 -0
  133. package/dist/fx/effects/magic-aura.json +47 -0
  134. package/dist/fx/effects/smoke-puff.json +34 -0
  135. package/dist/fx/emit-surface.d.ts +40 -0
  136. package/dist/fx/emit-surface.js +124 -0
  137. package/dist/fx/flicker.d.ts +30 -0
  138. package/dist/fx/flicker.js +42 -0
  139. package/dist/fx/index.d.ts +31 -0
  140. package/dist/fx/index.js +20 -0
  141. package/dist/fx/particles-wgsl.d.ts +57 -0
  142. package/dist/fx/particles-wgsl.js +828 -0
  143. package/dist/fx/particles.d.ts +566 -0
  144. package/dist/fx/particles.js +1533 -0
  145. package/dist/fx/random.d.ts +15 -0
  146. package/dist/fx/random.js +20 -0
  147. package/dist/fx/sky-wgsl.d.ts +23 -0
  148. package/dist/fx/sky-wgsl.js +127 -0
  149. package/dist/fx/sky.d.ts +96 -0
  150. package/dist/fx/sky.js +175 -0
  151. package/dist/fx/text-atlas.d.ts +143 -0
  152. package/dist/fx/text-atlas.js +327 -0
  153. package/dist/fx/trail-wgsl.d.ts +11 -0
  154. package/dist/fx/trail-wgsl.js +42 -0
  155. package/dist/fx/trail.d.ts +194 -0
  156. package/dist/fx/trail.js +555 -0
  157. package/dist/fx/water-wgsl.d.ts +34 -0
  158. package/dist/fx/water-wgsl.js +197 -0
  159. package/dist/fx/water.d.ts +176 -0
  160. package/dist/fx/water.js +371 -0
  161. package/dist/geom/collide/bvh.d.ts +151 -0
  162. package/dist/geom/collide/bvh.js +493 -0
  163. package/dist/geom/collide/debug.d.ts +14 -0
  164. package/dist/geom/collide/debug.js +74 -0
  165. package/dist/geom/collide/grid.d.ts +26 -0
  166. package/dist/geom/collide/grid.js +91 -0
  167. package/dist/geom/collide/index.d.ts +8 -0
  168. package/dist/geom/collide/index.js +8 -0
  169. package/dist/geom/collide/queries.d.ts +70 -0
  170. package/dist/geom/collide/queries.js +583 -0
  171. package/dist/geom/collide/serialize.d.ts +14 -0
  172. package/dist/geom/collide/serialize.js +145 -0
  173. package/dist/geom/collide/shapes.d.ts +161 -0
  174. package/dist/geom/collide/shapes.js +686 -0
  175. package/dist/geom/collide/slide.d.ts +66 -0
  176. package/dist/geom/collide/slide.js +375 -0
  177. package/dist/geom/collide/tile-slide.d.ts +88 -0
  178. package/dist/geom/collide/tile-slide.js +255 -0
  179. package/dist/geom/geometry.d.ts +95 -0
  180. package/dist/geom/geometry.js +322 -0
  181. package/dist/geom/heightfield.d.ts +72 -0
  182. package/dist/geom/heightfield.js +295 -0
  183. package/dist/geom/hull.d.ts +27 -0
  184. package/dist/geom/hull.js +279 -0
  185. package/dist/geom/index.d.ts +35 -0
  186. package/dist/geom/index.js +23 -0
  187. package/dist/geom/iso.d.ts +74 -0
  188. package/dist/geom/iso.js +89 -0
  189. package/dist/geom/lod.d.ts +29 -0
  190. package/dist/geom/lod.js +27 -0
  191. package/dist/geom/ops.d.ts +47 -0
  192. package/dist/geom/ops.js +285 -0
  193. package/dist/geom/primitives.d.ts +97 -0
  194. package/dist/geom/primitives.js +359 -0
  195. package/dist/geom/shapes.d.ts +43 -0
  196. package/dist/geom/shapes.js +88 -0
  197. package/dist/geom/simplify.d.ts +38 -0
  198. package/dist/geom/simplify.js +519 -0
  199. package/dist/geom/split.d.ts +37 -0
  200. package/dist/geom/split.js +413 -0
  201. package/dist/geom/sweep.d.ts +83 -0
  202. package/dist/geom/sweep.js +534 -0
  203. package/dist/geom/tangents.d.ts +16 -0
  204. package/dist/geom/tangents.js +124 -0
  205. package/dist/geom/triangulate.d.ts +19 -0
  206. package/dist/geom/triangulate.js +147 -0
  207. package/dist/host/default.d.ts +10 -0
  208. package/dist/host/default.js +10 -0
  209. package/dist/host/index.d.ts +4 -0
  210. package/dist/host/index.js +3 -0
  211. package/dist/host/types.d.ts +144 -0
  212. package/dist/host/types.js +1 -0
  213. package/dist/host/web-links.d.ts +38 -0
  214. package/dist/host/web-links.js +110 -0
  215. package/dist/host/web.d.ts +30 -0
  216. package/dist/host/web.js +216 -0
  217. package/dist/host/xr.d.ts +24 -0
  218. package/dist/host/xr.js +59 -0
  219. package/dist/i18n/i18n.d.ts +59 -0
  220. package/dist/i18n/i18n.js +241 -0
  221. package/dist/i18n/index.d.ts +3 -0
  222. package/dist/i18n/index.js +3 -0
  223. package/dist/i18n/message.d.ts +72 -0
  224. package/dist/i18n/message.js +369 -0
  225. package/dist/i18n/negotiate.d.ts +17 -0
  226. package/dist/i18n/negotiate.js +58 -0
  227. package/dist/index.d.ts +7 -0
  228. package/dist/index.js +1 -0
  229. package/dist/input/bindings.d.ts +31 -0
  230. package/dist/input/bindings.js +141 -0
  231. package/dist/input/clock.d.ts +44 -0
  232. package/dist/input/clock.js +49 -0
  233. package/dist/input/follow.d.ts +133 -0
  234. package/dist/input/follow.js +218 -0
  235. package/dist/input/gestures.d.ts +281 -0
  236. package/dist/input/gestures.js +662 -0
  237. package/dist/input/glyphs.d.ts +35 -0
  238. package/dist/input/glyphs.js +216 -0
  239. package/dist/input/gyro.d.ts +85 -0
  240. package/dist/input/gyro.js +140 -0
  241. package/dist/input/index.d.ts +19 -0
  242. package/dist/input/index.js +16 -0
  243. package/dist/input/input.d.ts +543 -0
  244. package/dist/input/input.js +1492 -0
  245. package/dist/input/look.d.ts +327 -0
  246. package/dist/input/look.js +681 -0
  247. package/dist/input/magnify.d.ts +177 -0
  248. package/dist/input/magnify.js +131 -0
  249. package/dist/input/pointer-dispatch.d.ts +138 -0
  250. package/dist/input/pointer-dispatch.js +180 -0
  251. package/dist/input/pointer-router.d.ts +204 -0
  252. package/dist/input/pointer-router.js +533 -0
  253. package/dist/input/pointer-sources.d.ts +164 -0
  254. package/dist/input/pointer-sources.js +306 -0
  255. package/dist/input/rays.d.ts +138 -0
  256. package/dist/input/rays.js +615 -0
  257. package/dist/input/record.d.ts +209 -0
  258. package/dist/input/record.js +601 -0
  259. package/dist/input/touch-zones.d.ts +147 -0
  260. package/dist/input/touch-zones.js +300 -0
  261. package/dist/input/types.d.ts +158 -0
  262. package/dist/input/types.js +1 -0
  263. package/dist/input/xr-source.d.ts +53 -0
  264. package/dist/input/xr-source.js +164 -0
  265. package/dist/nav/astar.d.ts +45 -0
  266. package/dist/nav/astar.js +182 -0
  267. package/dist/nav/corridor.d.ts +40 -0
  268. package/dist/nav/corridor.js +95 -0
  269. package/dist/nav/crowd.d.ts +94 -0
  270. package/dist/nav/crowd.js +624 -0
  271. package/dist/nav/flow.d.ts +61 -0
  272. package/dist/nav/flow.js +193 -0
  273. package/dist/nav/grid.d.ts +108 -0
  274. package/dist/nav/grid.js +210 -0
  275. package/dist/nav/heap.d.ts +22 -0
  276. package/dist/nav/heap.js +92 -0
  277. package/dist/nav/index.d.ts +37 -0
  278. package/dist/nav/index.js +19 -0
  279. package/dist/nav/mesh/areas.d.ts +44 -0
  280. package/dist/nav/mesh/areas.js +98 -0
  281. package/dist/nav/mesh/bake-options.d.ts +33 -0
  282. package/dist/nav/mesh/bake-options.js +76 -0
  283. package/dist/nav/mesh/contour.d.ts +22 -0
  284. package/dist/nav/mesh/contour.js +323 -0
  285. package/dist/nav/mesh/flow3.d.ts +35 -0
  286. package/dist/nav/mesh/flow3.js +229 -0
  287. package/dist/nav/mesh/links.d.ts +38 -0
  288. package/dist/nav/mesh/links.js +72 -0
  289. package/dist/nav/mesh/mesh-path.d.ts +152 -0
  290. package/dist/nav/mesh/mesh-path.js +643 -0
  291. package/dist/nav/mesh/mesh-reach.d.ts +78 -0
  292. package/dist/nav/mesh/mesh-reach.js +450 -0
  293. package/dist/nav/mesh/navmesh.d.ts +117 -0
  294. package/dist/nav/mesh/navmesh.js +647 -0
  295. package/dist/nav/mesh/polymesh.d.ts +24 -0
  296. package/dist/nav/mesh/polymesh.js +384 -0
  297. package/dist/nav/mesh/regions.d.ts +12 -0
  298. package/dist/nav/mesh/regions.js +367 -0
  299. package/dist/nav/mesh/serialize.d.ts +13 -0
  300. package/dist/nav/mesh/serialize.js +197 -0
  301. package/dist/nav/mesh/voxelize.d.ts +111 -0
  302. package/dist/nav/mesh/voxelize.js +552 -0
  303. package/dist/nav/obstacles.d.ts +75 -0
  304. package/dist/nav/obstacles.js +294 -0
  305. package/dist/nav/path.d.ts +59 -0
  306. package/dist/nav/path.js +177 -0
  307. package/dist/nav/query-queue.d.ts +46 -0
  308. package/dist/nav/query-queue.js +308 -0
  309. package/dist/nav/reach.d.ts +53 -0
  310. package/dist/nav/reach.js +148 -0
  311. package/dist/nav/steer.d.ts +36 -0
  312. package/dist/nav/steer.js +78 -0
  313. package/dist/platform/boot.d.ts +28 -0
  314. package/dist/platform/boot.js +43 -0
  315. package/dist/platform/device-mode.d.ts +40 -0
  316. package/dist/platform/device-mode.js +104 -0
  317. package/dist/platform/device.d.ts +31 -0
  318. package/dist/platform/device.js +91 -0
  319. package/dist/platform/environment.d.ts +42 -0
  320. package/dist/platform/environment.js +82 -0
  321. package/dist/platform/gpu.d.ts +40 -0
  322. package/dist/platform/gpu.js +57 -0
  323. package/dist/platform/index.d.ts +18 -0
  324. package/dist/platform/index.js +10 -0
  325. package/dist/platform/letterbox.d.ts +43 -0
  326. package/dist/platform/letterbox.js +57 -0
  327. package/dist/platform/links.d.ts +20 -0
  328. package/dist/platform/links.js +43 -0
  329. package/dist/platform/screens.d.ts +36 -0
  330. package/dist/platform/screens.js +138 -0
  331. package/dist/platform/surface.d.ts +72 -0
  332. package/dist/platform/surface.js +157 -0
  333. package/dist/platform/xr-fake.d.ts +168 -0
  334. package/dist/platform/xr-fake.js +434 -0
  335. package/dist/platform/xr-surface.d.ts +120 -0
  336. package/dist/platform/xr-surface.js +214 -0
  337. package/dist/platform/xr-types.d.ts +262 -0
  338. package/dist/post/ao.d.ts +12 -0
  339. package/dist/post/ao.js +136 -0
  340. package/dist/post/blend.d.ts +27 -0
  341. package/dist/post/blend.js +129 -0
  342. package/dist/post/depth-pass.d.ts +43 -0
  343. package/dist/post/depth-pass.js +118 -0
  344. package/dist/post/dof.d.ts +1 -0
  345. package/dist/post/dof.js +66 -0
  346. package/dist/post/exposure.d.ts +80 -0
  347. package/dist/post/exposure.js +322 -0
  348. package/dist/post/index.d.ts +12 -0
  349. package/dist/post/index.js +11 -0
  350. package/dist/post/lens-dirt.d.ts +13 -0
  351. package/dist/post/lens-dirt.js +91 -0
  352. package/dist/post/lut.d.ts +19 -0
  353. package/dist/post/lut.js +50 -0
  354. package/dist/post/motion-blur.d.ts +9 -0
  355. package/dist/post/motion-blur.js +78 -0
  356. package/dist/post/post-stack.d.ts +302 -0
  357. package/dist/post/post-stack.js +1137 -0
  358. package/dist/post/settings.d.ts +485 -0
  359. package/dist/post/settings.js +416 -0
  360. package/dist/post/shaders.d.ts +25 -0
  361. package/dist/post/shaders.js +410 -0
  362. package/dist/post/ssr.d.ts +16 -0
  363. package/dist/post/ssr.js +155 -0
  364. package/dist/post/taa.d.ts +74 -0
  365. package/dist/post/taa.js +235 -0
  366. package/dist/post/white-balance.d.ts +25 -0
  367. package/dist/post/white-balance.js +98 -0
  368. package/dist/render/bindings.d.ts +98 -0
  369. package/dist/render/bindings.js +281 -0
  370. package/dist/render/blend.d.ts +13 -0
  371. package/dist/render/blend.js +26 -0
  372. package/dist/render/camera-2d.d.ts +72 -0
  373. package/dist/render/camera-2d.js +92 -0
  374. package/dist/render/camera.d.ts +302 -0
  375. package/dist/render/camera.js +671 -0
  376. package/dist/render/color.d.ts +16 -0
  377. package/dist/render/color.js +41 -0
  378. package/dist/render/compressed.d.ts +104 -0
  379. package/dist/render/compressed.js +248 -0
  380. package/dist/render/diagnostics.d.ts +15 -0
  381. package/dist/render/diagnostics.js +43 -0
  382. package/dist/render/dynamic-resolution.d.ts +70 -0
  383. package/dist/render/dynamic-resolution.js +126 -0
  384. package/dist/render/fake-gpu.d.ts +204 -0
  385. package/dist/render/fake-gpu.js +426 -0
  386. package/dist/render/flipbook.d.ts +56 -0
  387. package/dist/render/flipbook.js +87 -0
  388. package/dist/render/frame.d.ts +278 -0
  389. package/dist/render/frame.js +174 -0
  390. package/dist/render/fullscreen.d.ts +54 -0
  391. package/dist/render/fullscreen.js +91 -0
  392. package/dist/render/gpu-timer.d.ts +73 -0
  393. package/dist/render/gpu-timer.js +186 -0
  394. package/dist/render/index.d.ts +53 -0
  395. package/dist/render/index.js +29 -0
  396. package/dist/render/instance-list.d.ts +185 -0
  397. package/dist/render/instance-list.js +547 -0
  398. package/dist/render/lights.d.ts +181 -0
  399. package/dist/render/lights.js +233 -0
  400. package/dist/render/lod.d.ts +54 -0
  401. package/dist/render/lod.js +52 -0
  402. package/dist/render/material.d.ts +165 -0
  403. package/dist/render/material.js +292 -0
  404. package/dist/render/mesh.d.ts +284 -0
  405. package/dist/render/mesh.js +472 -0
  406. package/dist/render/mipmap.d.ts +16 -0
  407. package/dist/render/mipmap.js +121 -0
  408. package/dist/render/parallax.d.ts +50 -0
  409. package/dist/render/parallax.js +47 -0
  410. package/dist/render/pbr-wgsl.d.ts +31 -0
  411. package/dist/render/pbr-wgsl.js +119 -0
  412. package/dist/render/ping-pong.d.ts +42 -0
  413. package/dist/render/ping-pong.js +74 -0
  414. package/dist/render/renderer.d.ts +741 -0
  415. package/dist/render/renderer.js +2486 -0
  416. package/dist/render/scene-pipelines.d.ts +44 -0
  417. package/dist/render/scene-pipelines.js +92 -0
  418. package/dist/render/scene-target.d.ts +170 -0
  419. package/dist/render/scene-target.js +450 -0
  420. package/dist/render/shadow.d.ts +74 -0
  421. package/dist/render/shadow.js +208 -0
  422. package/dist/render/skin.d.ts +112 -0
  423. package/dist/render/skin.js +240 -0
  424. package/dist/render/snapshot.d.ts +36 -0
  425. package/dist/render/snapshot.js +34 -0
  426. package/dist/render/standard.d.ts +162 -0
  427. package/dist/render/standard.js +263 -0
  428. package/dist/render/texture.d.ts +142 -0
  429. package/dist/render/texture.js +228 -0
  430. package/dist/render/tile-layer.d.ts +110 -0
  431. package/dist/render/tile-layer.js +304 -0
  432. package/dist/render/transform.d.ts +115 -0
  433. package/dist/render/transform.js +318 -0
  434. package/dist/render/wgsl.d.ts +131 -0
  435. package/dist/render/wgsl.js +528 -0
  436. package/dist/save/codec.d.ts +49 -0
  437. package/dist/save/codec.js +169 -0
  438. package/dist/save/index.d.ts +8 -0
  439. package/dist/save/index.js +8 -0
  440. package/dist/save/journal.d.ts +24 -0
  441. package/dist/save/journal.js +102 -0
  442. package/dist/save/migrate.d.ts +15 -0
  443. package/dist/save/migrate.js +37 -0
  444. package/dist/save/schema.d.ts +72 -0
  445. package/dist/save/schema.js +154 -0
  446. package/dist/save/settings.d.ts +28 -0
  447. package/dist/save/settings.js +59 -0
  448. package/dist/save/slots.d.ts +56 -0
  449. package/dist/save/slots.js +200 -0
  450. package/dist/save/storage.d.ts +35 -0
  451. package/dist/save/storage.js +62 -0
  452. package/dist/save/store.d.ts +114 -0
  453. package/dist/save/store.js +269 -0
  454. package/dist/testing/fake-host.d.ts +103 -0
  455. package/dist/testing/fake-host.js +163 -0
  456. package/dist/testing/fixed-clock.d.ts +25 -0
  457. package/dist/testing/fixed-clock.js +30 -0
  458. package/dist/testing/index.d.ts +4 -0
  459. package/dist/testing/index.js +5 -0
  460. package/dist/testing/test-game.d.ts +76 -0
  461. package/dist/testing/test-game.js +92 -0
  462. package/dist/tween/ease.d.ts +77 -0
  463. package/dist/tween/ease.js +192 -0
  464. package/dist/tween/index.d.ts +8 -0
  465. package/dist/tween/index.js +7 -0
  466. package/dist/tween/timeline.d.ts +78 -0
  467. package/dist/tween/timeline.js +269 -0
  468. package/dist/tween/tween.d.ts +168 -0
  469. package/dist/tween/tween.js +320 -0
  470. package/dist/ui/anchor.d.ts +206 -0
  471. package/dist/ui/anchor.js +307 -0
  472. package/dist/ui/caption.d.ts +122 -0
  473. package/dist/ui/caption.js +286 -0
  474. package/dist/ui/cursor.d.ts +105 -0
  475. package/dist/ui/cursor.js +261 -0
  476. package/dist/ui/dom.d.ts +30 -0
  477. package/dist/ui/dom.js +92 -0
  478. package/dist/ui/floats.d.ts +86 -0
  479. package/dist/ui/floats.js +260 -0
  480. package/dist/ui/hud.d.ts +198 -0
  481. package/dist/ui/hud.js +431 -0
  482. package/dist/ui/index.d.ts +37 -0
  483. package/dist/ui/index.js +34 -0
  484. package/dist/ui/inventory-card.d.ts +52 -0
  485. package/dist/ui/inventory-card.js +173 -0
  486. package/dist/ui/inventory-context.d.ts +29 -0
  487. package/dist/ui/inventory-context.js +125 -0
  488. package/dist/ui/inventory-drag.d.ts +63 -0
  489. package/dist/ui/inventory-drag.js +394 -0
  490. package/dist/ui/inventory-grid.d.ts +66 -0
  491. package/dist/ui/inventory-grid.js +625 -0
  492. package/dist/ui/inventory-radial.d.ts +39 -0
  493. package/dist/ui/inventory-radial.js +168 -0
  494. package/dist/ui/inventory.d.ts +128 -0
  495. package/dist/ui/inventory.js +154 -0
  496. package/dist/ui/layout.d.ts +356 -0
  497. package/dist/ui/layout.js +856 -0
  498. package/dist/ui/link.d.ts +64 -0
  499. package/dist/ui/link.js +155 -0
  500. package/dist/ui/loading.d.ts +40 -0
  501. package/dist/ui/loading.js +157 -0
  502. package/dist/ui/loupe.d.ts +74 -0
  503. package/dist/ui/loupe.js +164 -0
  504. package/dist/ui/menu-nav.d.ts +33 -0
  505. package/dist/ui/menu-nav.js +28 -0
  506. package/dist/ui/minimap-canvas.d.ts +145 -0
  507. package/dist/ui/minimap-canvas.js +460 -0
  508. package/dist/ui/notify.d.ts +40 -0
  509. package/dist/ui/notify.js +141 -0
  510. package/dist/ui/orb.d.ts +53 -0
  511. package/dist/ui/orb.js +304 -0
  512. package/dist/ui/overlay.d.ts +213 -0
  513. package/dist/ui/overlay.js +394 -0
  514. package/dist/ui/padnav.d.ts +53 -0
  515. package/dist/ui/padnav.js +146 -0
  516. package/dist/ui/perf.d.ts +130 -0
  517. package/dist/ui/perf.js +329 -0
  518. package/dist/ui/rotate-card.d.ts +54 -0
  519. package/dist/ui/rotate-card.js +97 -0
  520. package/dist/ui/stack.d.ts +128 -0
  521. package/dist/ui/stack.js +312 -0
  522. package/dist/ui/styles.d.ts +13 -0
  523. package/dist/ui/styles.js +200 -0
  524. package/dist/ui/text-scale.d.ts +67 -0
  525. package/dist/ui/text-scale.js +102 -0
  526. package/dist/ui/theme.d.ts +70 -0
  527. package/dist/ui/theme.js +98 -0
  528. package/dist/ui/toast.d.ts +23 -0
  529. package/dist/ui/toast.js +46 -0
  530. package/dist/ui/tooltip.d.ts +89 -0
  531. package/dist/ui/tooltip.js +333 -0
  532. package/dist/ui/widgets-2.d.ts +134 -0
  533. package/dist/ui/widgets-2.js +399 -0
  534. package/dist/ui/widgets-3.d.ts +91 -0
  535. package/dist/ui/widgets-3.js +212 -0
  536. package/dist/ui/widgets.d.ts +34 -0
  537. package/dist/ui/widgets.js +50 -0
  538. package/dist/ui/world-panel/index.d.ts +5 -0
  539. package/dist/ui/world-panel/index.js +5 -0
  540. package/dist/ui/world-panel/layout.d.ts +38 -0
  541. package/dist/ui/world-panel/layout.js +68 -0
  542. package/dist/ui/world-panel/paint.d.ts +96 -0
  543. package/dist/ui/world-panel/paint.js +136 -0
  544. package/dist/ui/world-panel/panel.d.ts +235 -0
  545. package/dist/ui/world-panel/panel.js +762 -0
  546. package/dist/ui/world-panel/wgsl.d.ts +10 -0
  547. package/dist/ui/world-panel/wgsl.js +52 -0
  548. package/dist/ui/world-panel/widgets.d.ts +207 -0
  549. package/dist/ui/world-panel/widgets.js +569 -0
  550. package/dist/voxel/chunks.d.ts +77 -0
  551. package/dist/voxel/chunks.js +149 -0
  552. package/dist/voxel/grid.d.ts +84 -0
  553. package/dist/voxel/grid.js +223 -0
  554. package/dist/voxel/index.d.ts +22 -0
  555. package/dist/voxel/index.js +7 -0
  556. package/dist/voxel/mesher-worker.d.ts +14 -0
  557. package/dist/voxel/mesher-worker.js +47 -0
  558. package/dist/voxel/mesher.d.ts +73 -0
  559. package/dist/voxel/mesher.js +306 -0
  560. package/dist/voxel/ray.d.ts +32 -0
  561. package/dist/voxel/ray.js +73 -0
  562. package/dist/voxel/wgsl.d.ts +16 -0
  563. package/dist/voxel/wgsl.js +28 -0
  564. package/dist/voxel/worker-client.d.ts +79 -0
  565. package/dist/voxel/worker-client.js +122 -0
  566. package/package.json +177 -3
@@ -0,0 +1,1137 @@
1
+ import { mat4 } from '../core/math.js';
2
+ import { AO_BLUR_WGSL, AO_WGSL } from './ao.js';
3
+ import { easeInOut, PostBlender } from './blend.js';
4
+ import { DepthPass } from './depth-pass.js';
5
+ import { DOF_WGSL } from './dof.js';
6
+ import { adaptExposure, ExposurePasses, exposureTarget } from './exposure.js';
7
+ import { MOTION_BLUR_WGSL } from './motion-blur.js';
8
+ import { applyPostPatch, cinematicPostSettings, defaultPostQuality, POST_UNIFORM_FLOATS, packPostUniforms, resolveGodRays, } from './settings.js';
9
+ import { COMPOSITE_WGSL, DOWN_WGSL, FXAA_WGSL, OUTLINE_WGSL, PREFILTER_WGSL, RAYS_WGSL, UP_WGSL, } from './shaders.js';
10
+ import { SSR_WGSL } from './ssr.js';
11
+ import { TAA_AUX_FORMAT, TAA_WGSL, Taa, taaReprojection } from './taa.js';
12
+ const HDR = 'rgba16float';
13
+ const AO = 'r8unorm';
14
+ const COMPOSITE_CLEAR = { r: 0, g: 0, b: 0, a: 1 };
15
+ /** The composite's texture slots: colour, emissive, bloom, rays, AO, DoF, outline, LUT (3D), SSR, dirt. */
16
+ const COMPOSITE_DIMS = [
17
+ '2d',
18
+ '2d',
19
+ '2d',
20
+ '2d',
21
+ '2d',
22
+ '2d',
23
+ '2d',
24
+ '3d',
25
+ '2d',
26
+ '2d',
27
+ ];
28
+ /** The jitter a detached stack's hook returns on the frame it clears itself (ws/post-7). */
29
+ const NO_JITTER = [0, 0];
30
+ /**
31
+ * Uniform staging slots for encodes with a viewport (ws/render-12): XR eyes resolved in one
32
+ * submit, each reading its own. Four covers a headset's two eyes and a quad-view layout.
33
+ */
34
+ const EYE_UNIFORM_SLOTS = 4;
35
+ /**
36
+ * TAA history slots (ws/xr-taa): the screen's plus one per eye viewport, so each XR eye
37
+ * reprojects against its own previous frame. Past this many views the least recently used
38
+ * slot is reset and reused.
39
+ */
40
+ const TAA_HISTORY_SLOTS = EYE_UNIFORM_SLOTS + 1;
41
+ /**
42
+ * The painterly post stack: bloom (mip chain), floor mist, god rays, ACES tonemap, colour
43
+ * grade, vignette, dither, plus ambient occlusion and depth of field over the scene depth when
44
+ * the frame has one (ws/post-depth; off by default), the finishing passes (ws/post-2, all
45
+ * off by default): a depth/normal outline, chromatic aberration, sharpen, a 3D LUT, film grain
46
+ * in the composite, and FXAA as one extra pass after it, and the third pass (ws/post-3, all
47
+ * off by default): TAA, screen-space reflections, auto-exposure, white balance, motion blur
48
+ * (camera, or per object from a velocity target since ws/post-4) and lens dirt. Plug it into a renderer with `renderer.post = new PostStack(r)`.
49
+ *
50
+ * Every pass reads the frame from `PostInputs` alone (ws/post-4): the view matrices and the
51
+ * jitter from `view`, the projection from `projection`, per-surface roughness and metalness
52
+ * from `roughMetal`, per-pixel motion from `velocity`. While TAA runs the stack declares
53
+ * `wantsJitter` and drives the Halton jitter through `renderer.setJitter` (ws/post-6), so every
54
+ * camera the renderer draws is jittered, XR eyes and offscreen views included.
55
+ *
56
+ * Settings are plain objects you can mutate at runtime (`post.settings.bloom.intensity = 2`)
57
+ * or patch with `post.set({ mist: { density: 0.4 } })`. Disabled passes are skipped entirely.
58
+ * `post.blendTo(patch, seconds, ease?)` eases numeric settings over time instead.
59
+ */
60
+ export class PostStack {
61
+ settings;
62
+ /**
63
+ * Cost caps (bloom levels, ray samples, fog octaves). Independent of `settings`, so scene
64
+ * presets and blends keep working under a lower quality tier. Change with `setQuality`.
65
+ */
66
+ quality = defaultPostQuality();
67
+ /**
68
+ * The TAA jitter sequence (ws/post-3): `post.taa.jitter(i)` is the `i`-th offset. While TAA
69
+ * runs the stack hands it to `renderer.setJitter` (ws/post-6).
70
+ */
71
+ taa = new Taa(8);
72
+ /**
73
+ * How many times a TAA history was reset (first frame, resize, re-enable). In XR each eye
74
+ * keeps its own history (ws/xr-taa), so each eye's reset counts.
75
+ */
76
+ historyResets = 0;
77
+ renderer;
78
+ uniforms = new Float32Array(POST_UNIFORM_FLOATS);
79
+ /**
80
+ * XR eyes (ws/render-12): every eye resolves on the frame's one encoder, so a `writeBuffer`
81
+ * per eye would leave each pass reading the last eye's uniforms at submit. An encode with a
82
+ * `viewport` writes its block into its own slot of this staging ring and copies it into each
83
+ * pass's buffer on the encoder, just before the pass. Made on the first such encode.
84
+ */
85
+ eyeUniforms = null;
86
+ /** The staging slot of the encode under way, or -1: write the passes' buffers directly. */
87
+ eyeSlot = -1;
88
+ eyeNext = 0;
89
+ prefilter;
90
+ down;
91
+ up;
92
+ rays;
93
+ composite;
94
+ /** Bilinear upscale of the scene-resolution composite (`quality.compositeAtScene`). */
95
+ upscale;
96
+ /** Depth passes (ws/post-depth): run only on frames with `inputs.depth` and the pass enabled. */
97
+ ao;
98
+ aoBlur;
99
+ dof;
100
+ /** Finishing passes (ws/post-2): FXAA after the composite, the outline mask before it. */
101
+ fxaa;
102
+ outline;
103
+ /** Post 3 (ws/post-3): the TAA resolve, the half-res SSR march and the motion-blur gather. */
104
+ taaPass;
105
+ ssrPass;
106
+ motionBlurPass;
107
+ /** The auto-exposure compute passes, made on the first frame in `'auto'` mode. */
108
+ exposurePasses = null;
109
+ bloomTex = null;
110
+ mipViews = [];
111
+ raysTex = null;
112
+ raysView = null;
113
+ levels = 0;
114
+ /** Levels asked for at the last resize (before the size clamp), to detect real changes. */
115
+ requestedLevels = 0;
116
+ width = 0;
117
+ height = 0;
118
+ boundEmissive = null;
119
+ boundDepth = null;
120
+ /** Bind groups per scene-colour view; cleared whenever any other input or target changes. */
121
+ groups = new Map();
122
+ /** Half-res AO (raw and blurred) and DoF targets, made on the first frame that needs them. */
123
+ aoTex = null;
124
+ aoView = null;
125
+ aoBlurTex = null;
126
+ aoBlurView = null;
127
+ dofTex = null;
128
+ dofView = null;
129
+ aoGroup = null;
130
+ aoBlurGroup = null;
131
+ /** Scene-size outline mask, made on the first frame that needs it; freed on resize. */
132
+ outlineTex = null;
133
+ outlineView = null;
134
+ outlineGroup = null;
135
+ warnedOutline = false;
136
+ /** Output-size FXAA input (the composite's result), made on the first frame that needs it. */
137
+ fxaaTex = null;
138
+ fxaaView = null;
139
+ fxaaGroup = null;
140
+ fxaaWidth = 0;
141
+ fxaaHeight = 0;
142
+ /** The 1×1×1 LUT bound while `lut.texture` is null, and the view of the bound LUT. */
143
+ lutPlaceholder;
144
+ lutPlaceholderView;
145
+ boundLut = null;
146
+ lutView;
147
+ /** The bound lens-dirt texture and its view (`null` → bloom mip 0 as a placeholder). */
148
+ boundDirt = null;
149
+ dirtView = null;
150
+ /** Seconds of frame time while `grain.animated`: the grain's seed. */
151
+ grainSeed = 0;
152
+ /**
153
+ * TAA history slots (ws/post-3; per view since ws/xr-taa): the screen's, and one per XR eye
154
+ * viewport, each with its scene-size history pair and aux pair (ws/post-5), made on the
155
+ * view's first TAA frame. At most `TAA_HISTORY_SLOTS`; no allocation once every view has one.
156
+ */
157
+ histories = [];
158
+ /** Counts TAA resolves: `TaaHistory.used`. */
159
+ taaUse = 0;
160
+ /** `encode`'s extra target, reused (no allocation per frame). */
161
+ taaExtra = [];
162
+ /** Half-res SSR term and scene-size motion-blur result, made on the first frame that needs them. */
163
+ ssrTex = null;
164
+ ssrView = null;
165
+ motionBlurTex = null;
166
+ motionBlurView = null;
167
+ frameState = {
168
+ jitterX: 0,
169
+ jitterY: 0,
170
+ reset: 1,
171
+ reproj: mat4.create(),
172
+ autoExposure: 1,
173
+ roughMetal: 0,
174
+ objectVelocity: 0,
175
+ taaVelocity: 0,
176
+ };
177
+ /** The current EV (`exposure`), eased in auto mode, `log2(grade.exposure)` in manual. */
178
+ ev = 0;
179
+ taaOn = false;
180
+ taaVelocityOn = false;
181
+ ssrOn = false;
182
+ motionBlurOn = false;
183
+ depthInfo = {
184
+ near: 0,
185
+ far: 0,
186
+ invProj: new Float32Array(16),
187
+ projX: 1,
188
+ projY: 1,
189
+ outputWidth: 1,
190
+ ortho: false,
191
+ };
192
+ raysGroup = null;
193
+ downGroups = [];
194
+ upGroups = [];
195
+ /** Scene-size composite target (display-encoded, swapchain format) and its upscale group. */
196
+ ldrTex = null;
197
+ ldrView = null;
198
+ upscaleGroup = null;
199
+ blendConstant = { r: 1, g: 1, b: 1, a: 1 };
200
+ noClear = null;
201
+ blender = new PostBlender();
202
+ lastTime = null;
203
+ /**
204
+ * The hook the stack gives `renderer.setJitter` while TAA runs (made once: no per-frame
205
+ * closure). It runs in `beginFrame`, so only while it is installed, and checks there that the
206
+ * stack is still attached (ws/post-7): a stack whose TAA resolve did not run during the last
207
+ * frame, or a renderer with no post processor, has been detached, and the hook clears itself
208
+ * before the frame draws.
209
+ */
210
+ jitterFn = (i) => {
211
+ if (this.jitterResolved && this.renderer.post !== null) {
212
+ this.jitterResolved = false;
213
+ return this.taa.jitter(i);
214
+ }
215
+ this.releaseJitter();
216
+ return NO_JITTER;
217
+ };
218
+ /** True while the stack has set `renderer.setJitter` (cleared once TAA stops or on `destroy`). */
219
+ jitterHooked = false;
220
+ /** A TAA resolve ran since the hook last ran (ws/post-7: the stack is still attached). */
221
+ jitterResolved = false;
222
+ /** The resolved god-ray source (x, y, visibility) when `godRays.direction` placed it. */
223
+ raySrc = new Float32Array(3);
224
+ boundRoughMetal = null;
225
+ boundVelocity = null;
226
+ boundTaaVelocity = null;
227
+ /** `godRays.direction` through the frame's view, or null when `x`/`y` place the rays. */
228
+ raySource(s, view, logicalWidth, logicalHeight) {
229
+ const g = s.godRays;
230
+ if (!g.direction || !view)
231
+ return null;
232
+ resolveGodRays(g, view.viewProj, logicalWidth, logicalHeight, this.raySrc);
233
+ return this.raySrc;
234
+ }
235
+ constructor(renderer, patch = {}) {
236
+ this.renderer = renderer;
237
+ this.settings = applyPostPatch(cinematicPostSettings(), patch);
238
+ const bytes = POST_UNIFORM_FLOATS * 4;
239
+ this.prefilter = renderer.createFullscreenPass({
240
+ label: 'post-bloom-prefilter',
241
+ wgsl: PREFILTER_WGSL,
242
+ targets: [HDR],
243
+ uniformBytes: bytes,
244
+ textures: 2,
245
+ });
246
+ this.down = renderer.createFullscreenPass({
247
+ label: 'post-bloom-down',
248
+ wgsl: DOWN_WGSL,
249
+ targets: [HDR],
250
+ textures: 1,
251
+ });
252
+ this.up = renderer.createFullscreenPass({
253
+ label: 'post-bloom-up',
254
+ wgsl: UP_WGSL,
255
+ targets: [HDR],
256
+ textures: 1,
257
+ blend: {
258
+ color: { srcFactor: 'constant', dstFactor: 'one', operation: 'add' },
259
+ alpha: { srcFactor: 'zero', dstFactor: 'one', operation: 'add' },
260
+ },
261
+ });
262
+ this.rays = renderer.createFullscreenPass({
263
+ label: 'post-godrays',
264
+ wgsl: RAYS_WGSL,
265
+ targets: [HDR],
266
+ uniformBytes: bytes,
267
+ textures: 1,
268
+ });
269
+ this.composite = renderer.createFullscreenPass({
270
+ label: 'post-composite',
271
+ wgsl: COMPOSITE_WGSL,
272
+ targets: [renderer.format],
273
+ uniformBytes: bytes,
274
+ textures: COMPOSITE_DIMS.length,
275
+ viewDimension: COMPOSITE_DIMS,
276
+ });
277
+ this.fxaa = renderer.createFullscreenPass({
278
+ label: 'post-fxaa',
279
+ wgsl: FXAA_WGSL,
280
+ targets: [renderer.format],
281
+ uniformBytes: bytes,
282
+ textures: 1,
283
+ });
284
+ this.outline = new DepthPass(renderer, {
285
+ label: 'post-outline',
286
+ wgsl: OUTLINE_WGSL,
287
+ format: AO,
288
+ textures: 0,
289
+ });
290
+ this.lutPlaceholder = renderer.device.createTexture({
291
+ label: 'post-lut-none',
292
+ size: [1, 1, 1],
293
+ dimension: '3d',
294
+ format: 'rgba8unorm',
295
+ usage: GPUTextureUsage.TEXTURE_BINDING,
296
+ });
297
+ this.lutPlaceholderView = this.lutPlaceholder.createView({ dimension: '3d' });
298
+ this.lutView = this.lutPlaceholderView;
299
+ this.ao = new DepthPass(renderer, { label: 'post-ao', wgsl: AO_WGSL, format: AO, textures: 0 });
300
+ this.aoBlur = new DepthPass(renderer, {
301
+ label: 'post-ao-blur',
302
+ wgsl: AO_BLUR_WGSL,
303
+ format: AO,
304
+ textures: 1,
305
+ });
306
+ this.dof = new DepthPass(renderer, {
307
+ label: 'post-dof',
308
+ wgsl: DOF_WGSL,
309
+ format: HDR,
310
+ textures: 1,
311
+ });
312
+ this.taaPass = new DepthPass(renderer, {
313
+ label: 'post-taa',
314
+ wgsl: TAA_WGSL,
315
+ format: [HDR, TAA_AUX_FORMAT],
316
+ textures: 4,
317
+ });
318
+ this.ssrPass = new DepthPass(renderer, {
319
+ label: 'post-ssr',
320
+ wgsl: SSR_WGSL,
321
+ format: HDR,
322
+ textures: 2,
323
+ });
324
+ this.motionBlurPass = new DepthPass(renderer, {
325
+ label: 'post-motion-blur',
326
+ wgsl: MOTION_BLUR_WGSL,
327
+ format: HDR,
328
+ textures: 2,
329
+ });
330
+ this.upscale = renderer.createFullscreenPass({
331
+ label: 'post-upscale',
332
+ targets: [renderer.format],
333
+ textures: 1,
334
+ wgsl: /* wgsl */ `
335
+ @group(0) @binding(1) var samp: sampler;
336
+ @group(0) @binding(2) var src: texture_2d<f32>;
337
+ @fragment fn fs_main(in: FullscreenIn) -> @location(0) vec4f {
338
+ return vec4f(textureSampleLevel(src, samp, in.uv, 0.0).rgb, 1.0);
339
+ }`,
340
+ });
341
+ this.resize(renderer.width, renderer.height);
342
+ }
343
+ /** Change the cost caps. Bloom level changes reallocate the mip chain on the next frame. */
344
+ setQuality(patch) {
345
+ Object.assign(this.quality, patch);
346
+ return this;
347
+ }
348
+ /** Bloom levels actually used: the setting, capped by quality, clamped to 2..8. */
349
+ wantLevels() {
350
+ const want = Math.min(this.settings.bloom.levels, this.quality.maxBloomLevels);
351
+ return Math.max(2, Math.min(8, Math.floor(want)));
352
+ }
353
+ /** Patch settings section by section. */
354
+ set(patch) {
355
+ this.blender.cancel(patch);
356
+ applyPostPatch(this.settings, patch);
357
+ return this;
358
+ }
359
+ /**
360
+ * Ease settings toward `patch` over `seconds` of frame time (default ease: smoothstep).
361
+ * Numbers and RGB colours interpolate from their current values. `enabled: true` applies
362
+ * at once, so set the pass's strength to 0 first to fade it in. `enabled: false`, `tonemap`,
363
+ * `dither` and `bloom.levels` switch at the end. A later `set` or `blendTo` on the same
364
+ * key takes over from wherever the blend is. `seconds <= 0` behaves like `set`.
365
+ */
366
+ blendTo(patch, seconds, ease = easeInOut) {
367
+ this.blender.blendTo(this.settings, patch, seconds, ease);
368
+ return this;
369
+ }
370
+ /** True while a `blendTo` is still running. */
371
+ get blending() {
372
+ return this.blender.active;
373
+ }
374
+ /** Jump every running blend to its end value. */
375
+ finishBlend() {
376
+ this.blender.finish(this.settings);
377
+ return this;
378
+ }
379
+ /**
380
+ * The current exposure in EV (ws/post-3): the eased auto-exposure value while
381
+ * `exposure.mode` is `'auto'`, else `log2(grade.exposure)`. The multiplier is `2^EV`.
382
+ */
383
+ get exposure() {
384
+ return this.ev;
385
+ }
386
+ /**
387
+ * The renderer reads this before each view (`PostProcessor.wantsJitter`): true while TAA is
388
+ * enabled and allowed by `quality.taa`, so the cameras' `setJitter` reaches their projection.
389
+ */
390
+ get wantsJitter() {
391
+ return this.settings.taa.enabled && this.quality.taa;
392
+ }
393
+ /** True when the last `encode` ran the TAA resolve (depth present, enabled, allowed). */
394
+ get taaActive() {
395
+ return this.taaOn;
396
+ }
397
+ /**
398
+ * True when the last TAA resolve reprojected by the velocity target (ws/post-5):
399
+ * `taa.reprojection` `'velocity'` and the frame had one.
400
+ */
401
+ get taaVelocityActive() {
402
+ return this.taaVelocityOn;
403
+ }
404
+ /** True when the last `encode` ran the SSR march. */
405
+ get ssrActive() {
406
+ return this.ssrOn;
407
+ }
408
+ /** True when the last `encode` ran the motion-blur gather. */
409
+ get motionBlurActive() {
410
+ return this.motionBlurOn;
411
+ }
412
+ /**
413
+ * Advance running blends to frame time `time` (seconds). `encode` calls this; call it
414
+ * yourself only when driving the stack without rendering. Steps are capped at 0.1 s, so
415
+ * a hidden tab or long hitch slows a blend rather than skipping it.
416
+ */
417
+ advanceBlend(time) {
418
+ const dt = this.lastTime === null ? 0 : Math.min(0.1, Math.max(0, time - this.lastTime));
419
+ this.lastTime = time;
420
+ if (this.blender.active)
421
+ this.blender.advance(this.settings, dt);
422
+ }
423
+ resize(width, height) {
424
+ const levels = this.wantLevels();
425
+ if (width === this.width && height === this.height && levels === this.requestedLevels)
426
+ return;
427
+ this.requestedLevels = levels;
428
+ this.width = width;
429
+ this.height = height;
430
+ const device = this.renderer.device;
431
+ this.bloomTex?.destroy();
432
+ this.raysTex?.destroy();
433
+ const bw = Math.max(1, width >> 1);
434
+ const bh = Math.max(1, height >> 1);
435
+ const maxLevels = Math.max(1, Math.floor(Math.log2(Math.min(bw, bh))) - 1);
436
+ this.levels = Math.max(1, Math.min(levels, maxLevels));
437
+ const usage = GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING;
438
+ this.bloomTex = device.createTexture({
439
+ label: 'post-bloom',
440
+ size: [bw, bh],
441
+ format: HDR,
442
+ mipLevelCount: this.levels,
443
+ usage,
444
+ });
445
+ this.mipViews = [];
446
+ for (let i = 0; i < this.levels; i++) {
447
+ this.mipViews.push(this.bloomTex.createView({ baseMipLevel: i, mipLevelCount: 1 }));
448
+ }
449
+ this.raysTex = device.createTexture({ label: 'post-rays', size: [bw, bh], format: HDR, usage });
450
+ this.raysView = this.raysTex.createView();
451
+ this.downGroups = [];
452
+ this.upGroups = [];
453
+ for (let i = 1; i < this.levels; i++) {
454
+ this.downGroups[i] = this.down.createBindGroup([this.mipViews[i - 1]]);
455
+ }
456
+ for (let i = 0; i < this.levels - 1; i++) {
457
+ this.upGroups[i] = this.up.createBindGroup([this.mipViews[i + 1]]);
458
+ }
459
+ this.raysGroup = this.rays.createBindGroup([this.mipViews[0]]);
460
+ this.freeLdr();
461
+ // Depth targets, the FXAA input and the temporal targets come back on the next frame that
462
+ // needs them, at the new size. The TAA history is gone with them.
463
+ this.freeDepthTargets();
464
+ this.freeFxaa();
465
+ this.freeTemporal();
466
+ this.invalidateGroups();
467
+ }
468
+ /**
469
+ * Record the stack from `inputs` into `output`. With `viewport` (ws/xr-post: one eye of a
470
+ * shared XR layer) the composite covers that rect only and loads the rest of the output;
471
+ * otherwise it clears and covers the whole output. Inputs of another size than the last
472
+ * `resize` (an eye-sized target through `renderer.resolve`) resize the stack first, so the
473
+ * same stack serves the screen and a headset in turn.
474
+ */
475
+ encode(encoder, inputs, output, viewport) {
476
+ this.advanceBlend(inputs.time);
477
+ const s = this.settings;
478
+ const q = this.quality;
479
+ const r = this.renderer;
480
+ const iw = inputs.color.width;
481
+ const ih = inputs.color.height;
482
+ if (iw !== this.width || ih !== this.height || this.wantLevels() !== this.requestedLevels) {
483
+ this.resize(iw, ih);
484
+ }
485
+ const outW = viewport ? viewport.width : r.outputWidth;
486
+ const outH = viewport ? viewport.height : r.outputHeight;
487
+ const clear = viewport ? this.noClear : COMPOSITE_CLEAR;
488
+ const dt = Math.min(0.1, Math.max(0, inputs.dt));
489
+ // Depth passes only on a frame that rendered depth (never the 2D path).
490
+ const proj = inputs.projection;
491
+ const depth = proj && inputs.depth ? inputs.depth : null;
492
+ const wantAo = depth !== null && s.ao.enabled;
493
+ const wantDof = depth !== null && s.dof.enabled;
494
+ const wantOutline = depth !== null && s.outline.enabled && q.outline;
495
+ if (s.outline.enabled && depth === null && !this.warnedOutline) {
496
+ this.warnedOutline = true;
497
+ console.warn('[post] outline needs the scene depth; this frame has none, so it is off');
498
+ }
499
+ // Post 3: the temporal passes need the depth; they reproject through `inputs.view` (an
500
+ // unknown view reprojects to itself).
501
+ const view = inputs.view;
502
+ const wantTaa = depth !== null && s.taa.enabled && q.taa;
503
+ const wantMotionBlur = depth !== null && s.motionBlur.enabled && q.motionBlur;
504
+ const wantSsr = depth !== null && s.ssr.enabled && q.ssr;
505
+ const wantAuto = s.exposure.mode === 'auto' && q.autoExposure;
506
+ const wantFxaa = s.fxaa.enabled && q.fxaa && !wantTaa;
507
+ if (wantAo && !this.aoTex)
508
+ this.allocAo();
509
+ if (wantDof && !this.dofTex)
510
+ this.allocDof();
511
+ if (wantOutline && !this.outlineTex)
512
+ this.allocOutline();
513
+ if (wantFxaa && (outW !== this.fxaaWidth || outH !== this.fxaaHeight))
514
+ this.allocFxaa(outW, outH);
515
+ const hist = wantTaa ? this.historyFor(viewport) : null;
516
+ if (hist && !hist.tex)
517
+ this.allocHistory(hist);
518
+ if (wantSsr && !this.ssrTex)
519
+ this.allocSsr();
520
+ if (wantMotionBlur && !this.motionBlurTex)
521
+ this.allocMotionBlur();
522
+ if (wantAuto && !this.exposurePasses)
523
+ this.exposurePasses = new ExposurePasses(r);
524
+ this.bindLut(s.lut.texture);
525
+ this.bindDirt(s.bloom.dirt?.texture ?? null);
526
+ // Per-surface inputs (ws/post-4): bound only while a pass that reads them runs.
527
+ const roughMetal = wantSsr && inputs.roughMetal ? inputs.roughMetal.view : null;
528
+ const objectVelocity = wantMotionBlur && s.motionBlur.mode === 'object' && inputs.velocity
529
+ ? inputs.velocity.view
530
+ : null;
531
+ const taaVelocity = wantTaa && s.taa.reprojection === 'velocity' && inputs.velocity ? inputs.velocity.view : null;
532
+ this.bindInputs(inputs.emissive.view, depth ? depth.view : null, roughMetal, objectVelocity, taaVelocity);
533
+ if (s.grain.animated && s.grain.intensity > 0)
534
+ this.grainSeed += dt;
535
+ let depthInfo = null;
536
+ if (depth && proj) {
537
+ depthInfo = this.depthInfo;
538
+ depthInfo.near = proj.near;
539
+ depthInfo.far = proj.far;
540
+ depthInfo.projX = proj.proj[0];
541
+ depthInfo.projY = proj.proj[5];
542
+ depthInfo.outputWidth = outW;
543
+ depthInfo.ortho = proj.proj[11] === 0;
544
+ if (!mat4.invert(depthInfo.invProj, proj.proj))
545
+ depthInfo = null;
546
+ }
547
+ // Post 3 frame state, from the view (ws/post-4): this frame's (jittered) clip → the previous
548
+ // frame's clip, the jitter the camera drew with, the EV, the per-surface input flags.
549
+ const fs = this.frameState;
550
+ if (!wantTaa)
551
+ this.invalidateHistories();
552
+ if (depthInfo && view) {
553
+ taaReprojection(fs.reproj, view.previousViewProj, view.invViewProj);
554
+ }
555
+ else {
556
+ mat4.identity(fs.reproj);
557
+ }
558
+ const reset = hist !== null && !hist.valid;
559
+ if (reset)
560
+ this.historyResets++;
561
+ fs.reset = reset ? 1 : 0;
562
+ fs.jitterX = wantTaa && view ? view.jitter[0] / iw : 0;
563
+ fs.jitterY = wantTaa && view ? view.jitter[1] / ih : 0;
564
+ fs.roughMetal = roughMetal ? 1 : 0;
565
+ fs.objectVelocity = objectVelocity ? 1 : 0;
566
+ fs.taaVelocity = taaVelocity ? 1 : 0;
567
+ this.updateExposure(wantAuto, dt);
568
+ packPostUniforms(this.uniforms, s, inputs.time, this.width, this.height, r.logicalWidth, r.logicalHeight, q, depthInfo, this.grainSeed, fs, this.raySource(s, view, r.logicalWidth, r.logicalHeight));
569
+ this.beginUploads(viewport);
570
+ const needBloomChain = s.bloom.enabled || (s.godRays.enabled && s.godRays.bloomSource > 0);
571
+ this.upload(encoder, this.composite.bindings.uniformBuffer, this.composite);
572
+ // GPU timing labels (undefined unless this frame is sampled; see Renderer.gpuTimer).
573
+ const timer = r.gpuTimer;
574
+ if (wantOutline && depthInfo) {
575
+ this.upload(encoder, this.outline.uniformBuffer, this.outline);
576
+ this.outline.encode(encoder, this.outlineView, this.outlineGroup, timer?.pass('outline'));
577
+ }
578
+ if (wantAo && depthInfo) {
579
+ this.upload(encoder, this.ao.uniformBuffer, this.ao);
580
+ this.upload(encoder, this.aoBlur.uniformBuffer, this.aoBlur);
581
+ this.ao.encode(encoder, this.aoView, this.aoGroup, timer?.pass('ao'));
582
+ this.aoBlur.encode(encoder, this.aoBlurView, this.aoBlurGroup, timer?.pass('ao'));
583
+ }
584
+ // The scene colour the rest of the stack reads: the raw colour, the TAA resolve, or the
585
+ // motion-blur gather over either.
586
+ const raw = inputs.color.view;
587
+ let scene = raw;
588
+ const depthView = depth ? depth.view : null;
589
+ this.taaOn = wantTaa && depthInfo !== null;
590
+ if (this.taaOn && depthView && hist) {
591
+ // This view's own history (ws/xr-taa): each eye reads and writes its slot's pair only.
592
+ const read = hist.index;
593
+ const write = 1 - read;
594
+ const group = this.taaGroup(raw, depthView, hist, read);
595
+ this.upload(encoder, this.taaPass.uniformBuffer, this.taaPass);
596
+ this.taaExtra[0] = hist.auxViews[write];
597
+ this.taaPass.encode(encoder, hist.views[write], group, timer?.pass('taa'), this.taaExtra);
598
+ hist.index = write;
599
+ hist.valid = true;
600
+ hist.used = ++this.taaUse;
601
+ scene = hist.views[write];
602
+ }
603
+ this.taaVelocityOn = this.taaOn && taaVelocity !== null;
604
+ this.syncJitter(this.taaOn);
605
+ this.motionBlurOn = wantMotionBlur && depthInfo !== null;
606
+ if (this.motionBlurOn && depthView) {
607
+ const group = this.motionBlurGroup(scene, depthView);
608
+ this.upload(encoder, this.motionBlurPass.uniformBuffer, this.motionBlurPass);
609
+ this.motionBlurPass.encode(encoder, this.motionBlurView, group, timer?.pass('motion-blur'));
610
+ scene = this.motionBlurView;
611
+ }
612
+ const groups = this.groupsFor(scene);
613
+ if (wantDof && depthInfo && depthView) {
614
+ this.upload(encoder, this.dof.uniformBuffer, this.dof);
615
+ this.dof.encode(encoder, this.dofView, this.dofGroup(groups, scene, depthView), timer?.pass('dof'));
616
+ }
617
+ this.ssrOn = wantSsr && depthInfo !== null;
618
+ if (this.ssrOn && depthView) {
619
+ this.upload(encoder, this.ssrPass.uniformBuffer, this.ssrPass);
620
+ this.ssrPass.encode(encoder, this.ssrView, this.ssrGroup(groups, scene, depthView), timer?.pass('ssr'));
621
+ }
622
+ if (wantAuto && this.exposurePasses) {
623
+ this.exposurePasses.encode(encoder, scene, this.width, this.height, s.exposure.histogramLow, s.exposure.histogramHigh);
624
+ }
625
+ if (needBloomChain) {
626
+ this.upload(encoder, this.prefilter.bindings.uniformBuffer, this.prefilter);
627
+ this.prefilter.encode(encoder, this.mipViews[0], {
628
+ bindGroup: this.prefilterGroup(groups, scene),
629
+ timestampWrites: timer?.pass('bloom'),
630
+ });
631
+ for (let i = 1; i < this.levels; i++) {
632
+ this.down.encode(encoder, this.mipViews[i], {
633
+ bindGroup: this.downGroups[i],
634
+ timestampWrites: timer?.pass('bloom'),
635
+ });
636
+ }
637
+ const k = Math.min(Math.max(s.bloom.scatter, 0), 1);
638
+ this.blendConstant.r = k;
639
+ this.blendConstant.g = k;
640
+ this.blendConstant.b = k;
641
+ for (let i = this.levels - 2; i >= 0; i--) {
642
+ this.up.encode(encoder, this.mipViews[i], {
643
+ bindGroup: this.upGroups[i],
644
+ clear: this.noClear,
645
+ blendConstant: this.blendConstant,
646
+ timestampWrites: timer?.pass('bloom'),
647
+ });
648
+ }
649
+ }
650
+ // When the chain is skipped, mip 0 is stale or zero; composite/rays ignore it then.
651
+ if (s.godRays.enabled) {
652
+ this.upload(encoder, this.rays.bindings.uniformBuffer, this.rays);
653
+ this.rays.encode(encoder, this.raysView, {
654
+ bindGroup: this.raysGroup,
655
+ timestampWrites: timer?.pass('rays'),
656
+ });
657
+ }
658
+ // With FXAA the composite (or its upscale) lands in an output-size texture the FXAA pass
659
+ // then resolves into the real output; without it, nothing changes.
660
+ const final = wantFxaa ? this.fxaaView : output;
661
+ const finalClear = wantFxaa ? COMPOSITE_CLEAR : clear;
662
+ const finalViewport = wantFxaa ? undefined : viewport;
663
+ const compositeGroup = this.compositeGroup(groups, scene);
664
+ if (this.quality.compositeAtScene && (this.width < outW || this.height < outH)) {
665
+ if (!this.ldrView)
666
+ this.allocLdr();
667
+ this.composite.encode(encoder, this.ldrView, {
668
+ bindGroup: compositeGroup,
669
+ clear: COMPOSITE_CLEAR,
670
+ timestampWrites: timer?.pass('composite'),
671
+ });
672
+ this.upscale.encode(encoder, final, {
673
+ bindGroup: this.upscaleGroup,
674
+ clear: finalClear,
675
+ viewport: finalViewport,
676
+ timestampWrites: timer?.pass('upscale'),
677
+ });
678
+ }
679
+ else {
680
+ this.composite.encode(encoder, final, {
681
+ bindGroup: compositeGroup,
682
+ clear: finalClear,
683
+ viewport: finalViewport,
684
+ timestampWrites: timer?.pass('composite'),
685
+ });
686
+ }
687
+ if (wantFxaa) {
688
+ this.upload(encoder, this.fxaa.bindings.uniformBuffer, this.fxaa);
689
+ this.fxaa.encode(encoder, output, {
690
+ bindGroup: this.fxaaGroup,
691
+ clear,
692
+ viewport,
693
+ timestampWrites: timer?.pass('fxaa'),
694
+ });
695
+ }
696
+ }
697
+ /**
698
+ * Pick how this encode's uniform block reaches the passes (ws/render-12). Without a viewport
699
+ * (the screen, one encode per submit) each pass's buffer is written directly, as before.
700
+ * With one (an XR eye; the eyes share a submit) the block goes into the next slot of a ring of
701
+ * `EYE_UNIFORM_SLOTS` and `upload` copies it on the encoder, so each eye's passes read that
702
+ * eye's reprojection, jitter and ray source. No allocation after the first eye.
703
+ */
704
+ beginUploads(viewport) {
705
+ if (!viewport) {
706
+ this.eyeSlot = -1;
707
+ return;
708
+ }
709
+ const bytes = this.uniforms.byteLength;
710
+ if (!this.eyeUniforms) {
711
+ this.eyeUniforms = this.renderer.device.createBuffer({
712
+ label: 'post-eye-uniforms',
713
+ size: bytes * EYE_UNIFORM_SLOTS,
714
+ usage: GPUBufferUsage.COPY_SRC | GPUBufferUsage.COPY_DST,
715
+ });
716
+ }
717
+ this.eyeSlot = this.eyeNext;
718
+ this.eyeNext = (this.eyeNext + 1) % EYE_UNIFORM_SLOTS;
719
+ const u = this.uniforms;
720
+ this.renderer.device.queue.writeBuffer(this.eyeUniforms, this.eyeSlot * bytes, u.buffer, u.byteOffset, bytes);
721
+ }
722
+ /** This encode's uniform block into `buffer` (the pass's): see `beginUploads`. */
723
+ upload(encoder, buffer, pass) {
724
+ if (this.eyeSlot < 0 || !this.eyeUniforms) {
725
+ pass.setUniforms(this.uniforms);
726
+ return;
727
+ }
728
+ const bytes = this.uniforms.byteLength;
729
+ encoder.copyBufferToBuffer(this.eyeUniforms, this.eyeSlot * bytes, buffer, 0, bytes);
730
+ }
731
+ /**
732
+ * After the first TAA frame, hand the Halton sequence to `renderer.setJitter` (the renderer
733
+ * runs it from frame 0 at the next `beginFrame`, so the sequence restarts each time TAA
734
+ * starts); once TAA stops, clear the hook with `null`, once. Only on those edges: a hook set
735
+ * by someone else in between is left alone. Ownership is read from the renderer (ws/post-8):
736
+ * the hook is this stack's while `renderer.jitter === this.jitterFn`. Another stack (or the
737
+ * page) that sets its hook on the same renderer takes it over: this one then never clears
738
+ * it, and sets its own again once the renderer has none.
739
+ */
740
+ syncJitter(taa) {
741
+ if (taa) {
742
+ this.jitterResolved = true;
743
+ if (this.jitterHooked && this.renderer.jitter !== null)
744
+ return;
745
+ this.jitterHooked = true;
746
+ this.renderer.setJitter(this.jitterFn);
747
+ }
748
+ else if (this.jitterHooked) {
749
+ this.releaseJitter();
750
+ }
751
+ }
752
+ /** Drop the hook: clear it with `null` if it is still this stack's, else leave it alone. */
753
+ releaseJitter() {
754
+ this.jitterHooked = false;
755
+ this.jitterResolved = false;
756
+ if (this.renderer.jitter !== this.jitterFn)
757
+ return;
758
+ this.renderer.setJitter(null);
759
+ }
760
+ /** Ease the EV toward the metered target in auto mode; mirror the manual value otherwise. */
761
+ updateExposure(auto, dt) {
762
+ const ex = this.settings.exposure;
763
+ if (!auto || !this.exposurePasses) {
764
+ this.ev = Math.log2(Math.max(1e-6, this.settings.grade.exposure));
765
+ this.frameState.autoExposure = 2 ** this.ev;
766
+ return;
767
+ }
768
+ const passes = this.exposurePasses;
769
+ passes.poll();
770
+ // Hold until the first histogram has been read back, then ease toward the metered target.
771
+ if (passes.readbacks > 0) {
772
+ const target = exposureTarget(passes.averageLog, ex.keyValue, ex.minEv, ex.maxEv);
773
+ this.ev = adaptExposure(this.ev, target, dt, ex.speedUp, ex.speedDown);
774
+ }
775
+ this.frameState.autoExposure = 2 ** this.ev;
776
+ }
777
+ /** Scene-size outline mask. Forces the bind groups to be rebuilt. */
778
+ allocOutline() {
779
+ this.outlineTex = this.renderer.device.createTexture({
780
+ label: 'post-outline',
781
+ size: [this.width, this.height],
782
+ format: AO,
783
+ usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING,
784
+ });
785
+ this.outlineView = this.outlineTex.createView();
786
+ this.invalidateGroups();
787
+ }
788
+ /** Output-size FXAA input, remade when the output size changes (a resize, or an XR eye). */
789
+ allocFxaa(width, height) {
790
+ this.fxaaTex?.destroy();
791
+ this.fxaaWidth = width;
792
+ this.fxaaHeight = height;
793
+ this.fxaaTex = this.renderer.device.createTexture({
794
+ label: 'post-fxaa',
795
+ size: [width, height],
796
+ format: this.renderer.format,
797
+ usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING,
798
+ });
799
+ this.fxaaView = this.fxaaTex.createView();
800
+ this.fxaaGroup = this.fxaa.createBindGroup([this.fxaaView]);
801
+ }
802
+ freeFxaa() {
803
+ this.fxaaTex?.destroy();
804
+ this.fxaaTex = null;
805
+ this.fxaaView = null;
806
+ this.fxaaGroup = null;
807
+ this.fxaaWidth = 0;
808
+ this.fxaaHeight = 0;
809
+ }
810
+ /**
811
+ * The TAA history slot of the view `viewport` names (ws/xr-taa): the screen's without one,
812
+ * else the eye whose rect it is. A new view takes a free slot, or past `TAA_HISTORY_SLOTS`
813
+ * the least recently used one (reset). No allocation once every view has a slot.
814
+ */
815
+ historyFor(viewport) {
816
+ const list = this.histories;
817
+ const has = viewport !== undefined;
818
+ for (let i = 0; i < list.length; i++) {
819
+ const h = list[i];
820
+ if (h.hasViewport !== has)
821
+ continue;
822
+ if (!viewport ||
823
+ (h.x === viewport.x &&
824
+ h.y === viewport.y &&
825
+ h.width === viewport.width &&
826
+ h.height === viewport.height)) {
827
+ return h;
828
+ }
829
+ }
830
+ let h;
831
+ if (list.length < TAA_HISTORY_SLOTS) {
832
+ h = {
833
+ hasViewport: has,
834
+ x: 0,
835
+ y: 0,
836
+ width: 0,
837
+ height: 0,
838
+ tex: null,
839
+ views: null,
840
+ auxTex: null,
841
+ auxViews: null,
842
+ index: 0,
843
+ valid: false,
844
+ used: 0,
845
+ };
846
+ list.push(h);
847
+ }
848
+ else {
849
+ h = list[0];
850
+ for (let i = 1; i < list.length; i++)
851
+ if (list[i].used < h.used)
852
+ h = list[i];
853
+ h.index = 0;
854
+ h.valid = false;
855
+ }
856
+ h.hasViewport = has;
857
+ h.x = viewport ? viewport.x : 0;
858
+ h.y = viewport ? viewport.y : 0;
859
+ h.width = viewport ? viewport.width : 0;
860
+ h.height = viewport ? viewport.height : 0;
861
+ return h;
862
+ }
863
+ /**
864
+ * The two scene-size TAA history textures of `h` (ws/post-3) and their aux pair. The first
865
+ * slot keeps the ws/post-3 labels (`post-taa-history-0/1`); slot `n` appends `-v<n>`.
866
+ */
867
+ allocHistory(h) {
868
+ const device = this.renderer.device;
869
+ const usage = GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING;
870
+ const size = [this.width, this.height];
871
+ const n = this.histories.indexOf(h);
872
+ const tag = n > 0 ? `-v${n}` : '';
873
+ const tex = (label, format) => device.createTexture({ label: label + tag, size, format, usage });
874
+ const a = tex('post-taa-history-0', HDR);
875
+ const b = tex('post-taa-history-1', HDR);
876
+ h.tex = [a, b];
877
+ h.views = [a.createView(), b.createView()];
878
+ const auxA = tex('post-taa-aux-0', TAA_AUX_FORMAT);
879
+ const auxB = tex('post-taa-aux-1', TAA_AUX_FORMAT);
880
+ h.auxTex = [auxA, auxB];
881
+ h.auxViews = [auxA.createView(), auxB.createView()];
882
+ h.index = 0;
883
+ h.valid = false;
884
+ this.invalidateGroups();
885
+ }
886
+ /** Mark every TAA history invalid: the next resolve of each view copies its frame. */
887
+ invalidateHistories() {
888
+ for (let i = 0; i < this.histories.length; i++)
889
+ this.histories[i].valid = false;
890
+ }
891
+ /** Half-res SSR term (premultiplied colour, fade in alpha). */
892
+ allocSsr() {
893
+ this.ssrTex = this.renderer.device.createTexture({
894
+ label: 'post-ssr',
895
+ size: [Math.max(1, this.width >> 1), Math.max(1, this.height >> 1)],
896
+ format: HDR,
897
+ usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING,
898
+ });
899
+ this.ssrView = this.ssrTex.createView();
900
+ this.invalidateGroups();
901
+ }
902
+ /** Scene-size motion-blur result. */
903
+ allocMotionBlur() {
904
+ this.motionBlurTex = this.renderer.device.createTexture({
905
+ label: 'post-motion-blur',
906
+ size: [this.width, this.height],
907
+ format: HDR,
908
+ usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING,
909
+ });
910
+ this.motionBlurView = this.motionBlurTex.createView();
911
+ this.invalidateGroups();
912
+ }
913
+ freeTemporal() {
914
+ for (let i = 0; i < this.histories.length; i++) {
915
+ const h = this.histories[i];
916
+ h.tex?.[0].destroy();
917
+ h.tex?.[1].destroy();
918
+ h.auxTex?.[0].destroy();
919
+ h.auxTex?.[1].destroy();
920
+ h.tex = null;
921
+ h.views = null;
922
+ h.auxTex = null;
923
+ h.auxViews = null;
924
+ h.valid = false;
925
+ }
926
+ this.ssrTex?.destroy();
927
+ this.ssrTex = null;
928
+ this.ssrView = null;
929
+ this.motionBlurTex?.destroy();
930
+ this.motionBlurTex = null;
931
+ this.motionBlurView = null;
932
+ this.exposurePasses?.invalidate();
933
+ }
934
+ /** Point the composite at `lut.texture` (or the placeholder); a change rebuilds the group. */
935
+ bindLut(texture) {
936
+ if (texture === this.boundLut)
937
+ return;
938
+ this.boundLut = texture;
939
+ this.lutView = texture ? texture.createView({ dimension: '3d' }) : this.lutPlaceholderView;
940
+ this.invalidateGroups();
941
+ }
942
+ /** Point the composite at the lens-dirt texture (or bloom mip 0 as a placeholder). */
943
+ bindDirt(texture) {
944
+ if (texture === this.boundDirt)
945
+ return;
946
+ this.boundDirt = texture;
947
+ this.dirtView = texture ? texture.createView() : null;
948
+ this.invalidateGroups();
949
+ }
950
+ /** Scene-size composite target, made on first use (a resize or tier change frees it). */
951
+ allocLdr() {
952
+ this.ldrTex = this.renderer.device.createTexture({
953
+ label: 'post-composite-scene',
954
+ size: [this.width, this.height],
955
+ format: this.renderer.format,
956
+ usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING,
957
+ });
958
+ this.ldrView = this.ldrTex.createView();
959
+ this.upscaleGroup = this.upscale.createBindGroup([this.ldrView]);
960
+ }
961
+ freeLdr() {
962
+ this.ldrTex?.destroy();
963
+ this.ldrTex = null;
964
+ this.ldrView = null;
965
+ this.upscaleGroup = null;
966
+ }
967
+ /** Half-res AO targets (raw and blurred). Forces the bind groups to be rebuilt. */
968
+ allocAo() {
969
+ const device = this.renderer.device;
970
+ const size = [Math.max(1, this.width >> 1), Math.max(1, this.height >> 1)];
971
+ const usage = GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING;
972
+ this.aoTex = device.createTexture({ label: 'post-ao', size, format: AO, usage });
973
+ this.aoView = this.aoTex.createView();
974
+ this.aoBlurTex = device.createTexture({ label: 'post-ao-blur', size, format: AO, usage });
975
+ this.aoBlurView = this.aoBlurTex.createView();
976
+ this.invalidateGroups();
977
+ }
978
+ /** Half-res DoF target: blurred colour, CoC in alpha. Forces the bind groups to be rebuilt. */
979
+ allocDof() {
980
+ const device = this.renderer.device;
981
+ const size = [Math.max(1, this.width >> 1), Math.max(1, this.height >> 1)];
982
+ const usage = GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING;
983
+ this.dofTex = device.createTexture({ label: 'post-dof', size, format: HDR, usage });
984
+ this.dofView = this.dofTex.createView();
985
+ this.invalidateGroups();
986
+ }
987
+ freeDepthTargets() {
988
+ this.aoTex?.destroy();
989
+ this.aoBlurTex?.destroy();
990
+ this.dofTex?.destroy();
991
+ this.outlineTex?.destroy();
992
+ this.aoTex = this.aoBlurTex = this.dofTex = this.outlineTex = null;
993
+ this.aoView = this.aoBlurView = this.dofView = this.outlineView = null;
994
+ this.aoGroup = this.aoBlurGroup = this.outlineGroup = null;
995
+ }
996
+ /** Drop every cached bind group: the next frame rebuilds the ones it uses. */
997
+ invalidateGroups() {
998
+ this.groups.clear();
999
+ this.boundEmissive = null;
1000
+ this.boundDepth = null;
1001
+ this.boundRoughMetal = null;
1002
+ this.boundVelocity = null;
1003
+ this.boundTaaVelocity = null;
1004
+ this.exposurePasses?.invalidate();
1005
+ }
1006
+ /** Track the emissive and depth views; a change drops the cached groups and rebuilds the depth-only ones. */
1007
+ bindInputs(emissive, depth, roughMetal, velocity, taaVelocity) {
1008
+ if (emissive === this.boundEmissive &&
1009
+ depth === this.boundDepth &&
1010
+ roughMetal === this.boundRoughMetal &&
1011
+ velocity === this.boundVelocity &&
1012
+ taaVelocity === this.boundTaaVelocity) {
1013
+ return;
1014
+ }
1015
+ this.groups.clear();
1016
+ this.boundEmissive = emissive;
1017
+ this.boundDepth = depth;
1018
+ this.boundRoughMetal = roughMetal;
1019
+ this.boundVelocity = velocity;
1020
+ this.boundTaaVelocity = taaVelocity;
1021
+ if (depth) {
1022
+ this.aoGroup = this.aoView ? this.ao.createBindGroup(depth, []) : null;
1023
+ this.aoBlurGroup = this.aoView ? this.aoBlur.createBindGroup(depth, [this.aoView]) : null;
1024
+ this.outlineGroup = this.outlineView ? this.outline.createBindGroup(depth, []) : null;
1025
+ }
1026
+ else {
1027
+ this.aoGroup = this.aoBlurGroup = this.outlineGroup = null;
1028
+ }
1029
+ }
1030
+ /** The cached groups for a scene-colour view (an empty set on first use). */
1031
+ groupsFor(scene) {
1032
+ let g = this.groups.get(scene);
1033
+ if (!g) {
1034
+ g = {
1035
+ prefilter: null,
1036
+ composite: null,
1037
+ dof: null,
1038
+ ssr: null,
1039
+ motionBlur: null,
1040
+ taa: new Array(TAA_HISTORY_SLOTS * 2).fill(null),
1041
+ };
1042
+ this.groups.set(scene, g);
1043
+ }
1044
+ return g;
1045
+ }
1046
+ prefilterGroup(g, scene) {
1047
+ if (!g.prefilter)
1048
+ g.prefilter = this.prefilter.createBindGroup([scene, this.boundEmissive]);
1049
+ return g.prefilter;
1050
+ }
1051
+ compositeGroup(g, scene) {
1052
+ if (!g.composite) {
1053
+ const mip0 = this.mipViews[0];
1054
+ g.composite = this.composite.createBindGroup([
1055
+ scene,
1056
+ this.boundEmissive,
1057
+ mip0,
1058
+ this.raysView,
1059
+ this.aoBlurView ?? mip0,
1060
+ this.dofView ?? mip0,
1061
+ this.outlineView ?? mip0,
1062
+ this.lutView,
1063
+ this.ssrView ?? mip0,
1064
+ this.dirtView ?? mip0,
1065
+ ]);
1066
+ }
1067
+ return g.composite;
1068
+ }
1069
+ dofGroup(g, scene, depth) {
1070
+ if (!g.dof)
1071
+ g.dof = this.dof.createBindGroup(depth, [scene]);
1072
+ return g.dof;
1073
+ }
1074
+ ssrGroup(g, scene, depth) {
1075
+ // Without the rough-metal target the scene colour stands in (the shader does not read it).
1076
+ if (!g.ssr)
1077
+ g.ssr = this.ssrPass.createBindGroup(depth, [scene, this.boundRoughMetal ?? scene]);
1078
+ return g.ssr;
1079
+ }
1080
+ motionBlurGroup(scene, depth) {
1081
+ const g = this.groupsFor(scene);
1082
+ // Without object velocity the scene colour stands in (the shader does not read it).
1083
+ if (!g.motionBlur) {
1084
+ g.motionBlur = this.motionBlurPass.createBindGroup(depth, [
1085
+ scene,
1086
+ this.boundVelocity ?? scene,
1087
+ ]);
1088
+ }
1089
+ return g.motionBlur;
1090
+ }
1091
+ /** The TAA resolve group reading the raw colour, history `read` of `h` and its aux. */
1092
+ taaGroup(raw, depth, h, read) {
1093
+ const g = this.groupsFor(raw);
1094
+ const key = this.histories.indexOf(h) * 2 + read;
1095
+ let group = g.taa[key];
1096
+ if (!group) {
1097
+ // Without a velocity target the raw colour stands in (the shader does not read it).
1098
+ group = this.taaPass.createBindGroup(depth, [
1099
+ raw,
1100
+ h.views[read],
1101
+ this.boundTaaVelocity ?? raw,
1102
+ h.auxViews[read],
1103
+ ]);
1104
+ g.taa[key] = group;
1105
+ }
1106
+ return group;
1107
+ }
1108
+ /**
1109
+ * Free the stack's GPU resources and clear the `renderer.setJitter` hook if TAA had set it
1110
+ * (ws/post-6). The stack is not usable afterwards.
1111
+ */
1112
+ destroy() {
1113
+ this.syncJitter(false);
1114
+ this.bloomTex?.destroy();
1115
+ this.raysTex?.destroy();
1116
+ this.freeDepthTargets();
1117
+ this.freeFxaa();
1118
+ this.freeTemporal();
1119
+ this.exposurePasses?.destroy();
1120
+ this.lutPlaceholder.destroy();
1121
+ this.ao.destroy();
1122
+ this.aoBlur.destroy();
1123
+ this.dof.destroy();
1124
+ this.outline.destroy();
1125
+ this.taaPass.destroy();
1126
+ this.ssrPass.destroy();
1127
+ this.motionBlurPass.destroy();
1128
+ this.fxaa.destroy();
1129
+ this.prefilter.destroy();
1130
+ this.rays.destroy();
1131
+ this.composite.destroy();
1132
+ this.eyeUniforms?.destroy();
1133
+ this.eyeUniforms = null;
1134
+ this.freeLdr();
1135
+ this.upscale.destroy();
1136
+ }
1137
+ }