@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,416 @@
1
+ /** Post-stack settings. All colours are linear RGB. Mutate in place at runtime; read each frame. */
2
+ import { mat4 } from '../core/math.js';
3
+ import { whiteBalanceMatrix } from './white-balance.js';
4
+ /**
5
+ * The tuned look the engine shipped with (ws/api-render names it): bloom, a cool floor mist,
6
+ * god rays from the top centre of a 1920×1080 design, a vignette and a slight saturation and
7
+ * contrast lift. `PostStack` and `createGame` start from it. A new settings object each call.
8
+ */
9
+ export function cinematicPostSettings() {
10
+ return {
11
+ bloom: {
12
+ enabled: true,
13
+ intensity: 0.9,
14
+ threshold: 1.2,
15
+ knee: 0.5,
16
+ emissiveWeight: 1,
17
+ scatter: 0.75,
18
+ levels: 6,
19
+ },
20
+ mist: {
21
+ enabled: true,
22
+ color: [0.11, 0.13, 0.19],
23
+ density: 0.65,
24
+ top: 0.55,
25
+ bottom: 1.0,
26
+ falloff: 1.6,
27
+ noiseScale: 3.0,
28
+ speed: 0.04,
29
+ lightScatter: 0.6,
30
+ },
31
+ godRays: {
32
+ enabled: true,
33
+ x: 960,
34
+ y: -80,
35
+ intensity: 0.45,
36
+ decay: 0.965,
37
+ density: 0.85,
38
+ weight: 0.035,
39
+ color: [0.7, 0.85, 1.0],
40
+ sourceRadius: 160,
41
+ sourceIntensity: 1.0,
42
+ bloomSource: 0.35,
43
+ },
44
+ vignette: { enabled: true, intensity: 0.65, radius: 0.55, softness: 0.55, color: [0, 0, 0] },
45
+ grade: {
46
+ enabled: true,
47
+ exposure: 1,
48
+ tonemap: 'aces',
49
+ lift: [0, 0, 0],
50
+ gamma: [1, 1, 1],
51
+ gain: [1, 1, 1],
52
+ saturation: 1.1,
53
+ contrast: 1.05,
54
+ dither: true,
55
+ },
56
+ ao: { enabled: false, radius: 0.5, intensity: 1, bias: 0.02, power: 1.5 },
57
+ dof: { enabled: false, focusDistance: 10, focusRange: 6, maxBlur: 8, nearBlur: true },
58
+ fxaa: { enabled: false, subpixel: 0.75, edgeThreshold: 0.125 },
59
+ lut: { texture: null, size: 32, intensity: 1 },
60
+ grain: { intensity: 0, size: 1, luminanceResponse: 0.5, animated: true },
61
+ aberration: { intensity: 0, radialOnly: true },
62
+ sharpen: { amount: 0 },
63
+ outline: {
64
+ enabled: false,
65
+ color: [0, 0, 0],
66
+ thickness: 1.5,
67
+ depthThreshold: 0.03,
68
+ normalThreshold: 0.4,
69
+ intensity: 1,
70
+ },
71
+ taa: {
72
+ enabled: false,
73
+ feedback: 0.9,
74
+ sharpen: 0.2,
75
+ clampMode: 'aabb',
76
+ reprojection: 'velocity',
77
+ depthReject: 0.1,
78
+ velocityReject: 2,
79
+ historyFilter: 'catmull-rom',
80
+ },
81
+ ssr: {
82
+ enabled: false,
83
+ maxSteps: 32,
84
+ thickness: 0.5,
85
+ maxDistance: 20,
86
+ roughnessCutoff: 0.6,
87
+ intensity: 1,
88
+ fadeEdges: 0.15,
89
+ },
90
+ exposure: {
91
+ mode: 'manual',
92
+ minEv: -4,
93
+ maxEv: 4,
94
+ speedUp: 3,
95
+ speedDown: 1,
96
+ keyValue: 0.18,
97
+ histogramLow: 0.4,
98
+ histogramHigh: 0.95,
99
+ },
100
+ whiteBalance: { temperature: 6500, tint: 0 },
101
+ motionBlur: { enabled: false, mode: 'object', intensity: 1, maxSamples: 8 },
102
+ };
103
+ }
104
+ /**
105
+ * A neutral preset (ws/api-render): every pass off but the tone map (ACES, exposure 1, no
106
+ * lift, gamma, gain, saturation or contrast change, dithered), so the image is the scene and
107
+ * nothing else. Start here and turn on what the game wants. A new settings object each call.
108
+ */
109
+ export function neutralPostSettings() {
110
+ const s = cinematicPostSettings();
111
+ s.bloom.enabled = false;
112
+ s.mist.enabled = false;
113
+ s.godRays.enabled = false;
114
+ s.vignette.enabled = false;
115
+ s.grade.saturation = 1;
116
+ s.grade.contrast = 1;
117
+ return s;
118
+ }
119
+ /**
120
+ * A deep copy of `s` (ws/post-4): every section and every array (`RGB`) is new, so mutating
121
+ * the copy or blending it never touches the original. GPU handles (`lut.texture`,
122
+ * `bloom.dirt.texture`) are shared, not copied.
123
+ */
124
+ export function clonePostSettings(s) {
125
+ const out = {};
126
+ for (const key of Object.keys(s))
127
+ out[key] = cloneValue(s[key]);
128
+ return out;
129
+ }
130
+ function cloneValue(v) {
131
+ if (Array.isArray(v))
132
+ return v.map(cloneValue);
133
+ if (v === null || typeof v !== 'object')
134
+ return v;
135
+ const out = {};
136
+ // `texture` fields are GPU handles: shared, never copied.
137
+ for (const [k, x] of Object.entries(v))
138
+ out[k] = k === 'texture' ? x : cloneValue(x);
139
+ return out;
140
+ }
141
+ /** Shallow-per-section merge of `patch` into `target` (mutates target). */
142
+ export function applyPostPatch(target, patch) {
143
+ for (const key of Object.keys(patch)) {
144
+ const section = patch[key];
145
+ if (section)
146
+ Object.assign(target[key], section);
147
+ }
148
+ return target;
149
+ }
150
+ export function defaultPostQuality() {
151
+ return {
152
+ maxBloomLevels: 8,
153
+ raySamples: 64,
154
+ fogOctaves: 5,
155
+ compositeAtScene: false,
156
+ aoSamples: 12,
157
+ dofTaps: 24,
158
+ fxaa: true,
159
+ outline: true,
160
+ taa: true,
161
+ ssr: true,
162
+ autoExposure: true,
163
+ motionBlur: true,
164
+ };
165
+ }
166
+ /**
167
+ * The low tier's caps: fewer bloom levels, samples and octaves; no FXAA, outline, SSR or
168
+ * motion-blur pass. TAA and auto-exposure stay (cheap, and TAA replaces FXAA).
169
+ */
170
+ export function lowPostQuality() {
171
+ return {
172
+ maxBloomLevels: 4,
173
+ raySamples: 24,
174
+ fogOctaves: 3,
175
+ compositeAtScene: true,
176
+ aoSamples: 6,
177
+ dofTaps: 12,
178
+ fxaa: false,
179
+ outline: false,
180
+ taa: true,
181
+ ssr: false,
182
+ autoExposure: true,
183
+ motionBlur: false,
184
+ };
185
+ }
186
+ /**
187
+ * The cost caps for a device class (ws/post-3): `'phone'` is `lowPostQuality()`; `'tablet'`
188
+ * trims the samples and skips SSR; `'desktop'` is everything; `'xr'` is full quality with
189
+ * TAA, SSR and motion blur off: frame budget and comfort first (SSR costs a march per eye;
190
+ * motion blur in a headset reads as lag). TAA keeps one history per eye since ws/xr-taa, so a
191
+ * game may opt in with `{ ...postQualityFor('xr'), taa: true }` and `taa.enabled`.
192
+ */
193
+ export function postQualityFor(tier) {
194
+ switch (tier) {
195
+ case 'phone':
196
+ return lowPostQuality();
197
+ case 'tablet':
198
+ return {
199
+ ...defaultPostQuality(),
200
+ maxBloomLevels: 6,
201
+ raySamples: 48,
202
+ aoSamples: 8,
203
+ dofTaps: 16,
204
+ ssr: false,
205
+ };
206
+ case 'xr':
207
+ return { ...defaultPostQuality(), taa: false, ssr: false, motionBlur: false };
208
+ default:
209
+ return defaultPostQuality();
210
+ }
211
+ }
212
+ /**
213
+ * Uniform layout shared by all post shaders: 22 × vec4f + mat4x4f + 17 × vec4f + mat4x4f +
214
+ * vec4f.
215
+ */
216
+ export const POST_UNIFORM_FLOATS = 192;
217
+ export const WGSL_POST_UNIFORMS = /* wgsl */ `
218
+ struct PostUniforms {
219
+ frame: vec4f, // time, aspect, 1/width, 1/height
220
+ enables: vec4f, // bloom, fog, rays, vignette
221
+ enables2: vec4f, // grade, tonemap (0 none, 1 aces, 2 reinhard), dither, exposure
222
+ bloom: vec4f, // intensity, threshold, knee, emissiveWeight
223
+ bloom2: vec4f, // scatter, -, -, -
224
+ fogColor: vec4f, // rgb, density
225
+ fog: vec4f, // top, bottom, noiseScale, speed
226
+ fog2: vec4f, // lightScatter, falloff, -, -
227
+ rays: vec4f, // light uv x, y, intensity, decay
228
+ rays2: vec4f, // density, weight, source radius (uv-y units), source intensity
229
+ raysColor: vec4f, // rgb, bloomSource
230
+ vignette: vec4f, // intensity, radius, softness, -
231
+ vignetteColor: vec4f,
232
+ lift: vec4f,
233
+ gamma: vec4f,
234
+ gain: vec4f,
235
+ grade: vec4f, // saturation, contrast, -, -
236
+ quality: vec4f, // ray samples, fog octaves, ortho projection (1, ws/post-4), -
237
+ ao: vec4f, // enabled (0 without depth), radius, intensity, bias
238
+ ao2: vec4f, // power, samples, proj x scale, proj y scale
239
+ dof: vec4f, // enabled (0 without depth), focus distance, focus range, max blur (uv of width)
240
+ dof2: vec4f, // near blur, taps, near plane, far plane
241
+ invProj: mat4x4f, // clip -> view
242
+ fxaa: vec4f, // enabled, subpixel, edgeThreshold, -
243
+ lut: vec4f, // enabled, (size-1)/size, intensity, 0.5/size
244
+ grain: vec4f, // intensity, cell size (px), luminanceResponse, seed
245
+ aberration: vec4f, // intensity (px), radialOnly, -, -
246
+ sharpen: vec4f, // amount, -, -, -
247
+ outline: vec4f, // enabled (0 without depth), thickness (scene px), depthThreshold, normalThreshold
248
+ outlineColor: vec4f, // rgb, intensity
249
+ // Post 3 (ws/post-3).
250
+ taa: vec4f, // enabled (0 without depth), feedback, sharpen, clampMode (0 aabb, 1 variance)
251
+ taa2: vec4f, // view jitter x (uv), view jitter y (uv), reset (1 = copy the current frame), velocity reprojection (1, ws/post-5)
252
+ ssr: vec4f, // enabled (0 without depth), maxSteps, thickness, maxDistance
253
+ ssr2: vec4f, // roughnessCutoff, intensity, fadeEdges (uv), rough-metal target (1)
254
+ exposure: vec4f, // auto (1), histogramLow, histogramHigh, keyValue
255
+ wb0: vec4f, // white-balance matrix row 0 (rgb weights), enabled
256
+ wb1: vec4f, // row 1
257
+ wb2: vec4f, // row 2
258
+ motionBlur: vec4f, // enabled (0 without depth), intensity, samples, velocity target (1 = object)
259
+ dirt: vec4f, // enabled, intensity, -, -
260
+ reproj: mat4x4f, // current clip -> previous frame's clip (identity on a reset)
261
+ // Post 5 (ws/post-5).
262
+ taa3: vec4f, // depthReject (fraction), velocityReject (px), history filter (1 = Catmull-Rom, ws/post-6), -
263
+ };
264
+ @group(0) @binding(0) var<uniform> P: PostUniforms;
265
+ `;
266
+ const TONEMAP_ID = { none: 0, aces: 1, reinhard: 2 };
267
+ /**
268
+ * Where a directional light's source sits on screen (ws/api-render): the point at infinity
269
+ * opposite `direction` (the way the light travels) through `viewProj` (column-major), in
270
+ * logical px of a `width × height` view, written to `out` as x, y. Returns 1 when the source is
271
+ * in front of the camera and 0 when it is behind (or the projection is orthographic, which has
272
+ * no point at infinity); `out` is left alone then. No allocation.
273
+ */
274
+ export function godRaySource(viewProj, direction, width, height, out) {
275
+ const x = -(direction[0] ?? 0);
276
+ const y = -(direction[1] ?? 0);
277
+ const z = -(direction[2] ?? 0);
278
+ const m = viewProj;
279
+ const w = m[3] * x + m[7] * y + m[11] * z;
280
+ if (!(w > 1e-6))
281
+ return 0;
282
+ const cx = (m[0] * x + m[4] * y + m[8] * z) / w;
283
+ const cy = (m[1] * x + m[5] * y + m[9] * z) / w;
284
+ out[0] = ((cx + 1) / 2) * width;
285
+ out[1] = ((1 - cy) / 2) * height;
286
+ return 1;
287
+ }
288
+ /**
289
+ * The god-ray source for this frame: `x`/`y`, or `godRaySource` of `direction` through `view`.
290
+ * Writes x, y and visibility into `out`.
291
+ */
292
+ export function resolveGodRays(r, viewProj, logicalWidth, logicalHeight, out) {
293
+ out[0] = r.x;
294
+ out[1] = r.y;
295
+ out[2] = 1;
296
+ if (r.direction && viewProj) {
297
+ out[2] = godRaySource(viewProj, r.direction, logicalWidth, logicalHeight, out);
298
+ }
299
+ }
300
+ /** Pack settings into the uniform array (no allocation). */
301
+ export function packPostUniforms(out, s, time, width, height, logicalWidth, logicalHeight, quality = DEFAULT_QUALITY, depth = null, grainSeed = 0, frame = null, rays = null) {
302
+ const b = s.bloom;
303
+ const f = s.mist;
304
+ const r = s.godRays;
305
+ const v = s.vignette;
306
+ const g = s.grade;
307
+ const auto = s.exposure.mode === 'auto' && quality.autoExposure && frame !== null;
308
+ let o = 0;
309
+ o = put(out, o, time, width / height, 1 / width, 1 / height);
310
+ o = put(out, o, +b.enabled, +f.enabled, +r.enabled, +v.enabled);
311
+ o = put(out, o, +g.enabled, g.enabled ? TONEMAP_ID[g.tonemap] : 1, +g.dither, auto ? frame.autoExposure : g.exposure);
312
+ o = put(out, o, b.intensity, b.threshold, b.knee, b.emissiveWeight);
313
+ o = put(out, o, b.scatter, 0, 0, 0);
314
+ o = put(out, o, f.color[0], f.color[1], f.color[2], f.density);
315
+ o = put(out, o, f.top, f.bottom, f.noiseScale, f.speed);
316
+ o = put(out, o, f.lightScatter, f.falloff, 0, 0);
317
+ // `rays` (x, y, visibility) is the resolved source when the stack placed it from `direction`.
318
+ const rx = rays ? rays[0] : r.x;
319
+ const ry = rays ? rays[1] : r.y;
320
+ const rv = rays ? rays[2] : 1;
321
+ o = put(out, o, rx / logicalWidth, ry / logicalHeight, r.intensity * rv, r.decay);
322
+ o = put(out, o, r.density, r.weight, r.sourceRadius / logicalHeight, r.sourceIntensity * rv);
323
+ o = put(out, o, r.color[0], r.color[1], r.color[2], r.bloomSource);
324
+ o = put(out, o, v.intensity, v.radius, v.softness, 0);
325
+ o = put(out, o, v.color[0], v.color[1], v.color[2], 0);
326
+ o = put(out, o, g.lift[0], g.lift[1], g.lift[2], 0);
327
+ o = put(out, o, g.gamma[0], g.gamma[1], g.gamma[2], 0);
328
+ o = put(out, o, g.gain[0], g.gain[1], g.gain[2], 0);
329
+ o = put(out, o, g.saturation, g.contrast, 0, 0);
330
+ o = put(out, o, Math.round(Math.min(64, Math.max(8, quality.raySamples))), Math.round(Math.min(5, Math.max(2, quality.fogOctaves))), depth?.ortho ? 1 : 0, 0);
331
+ const a = s.ao;
332
+ const d = s.dof;
333
+ const ao = depth !== null && a.enabled ? 1 : 0;
334
+ const dof = depth !== null && d.enabled ? 1 : 0;
335
+ o = put(out, o, ao, a.radius, a.intensity, a.bias);
336
+ o = put(out, o, a.power, Math.round(Math.min(16, Math.max(4, quality.aoSamples))), depth?.projX ?? 1, depth?.projY ?? 1);
337
+ const blurUv = depth ? d.maxBlur / Math.max(1, depth.outputWidth) : 0;
338
+ o = put(out, o, dof, d.focusDistance, Math.max(1e-3, d.focusRange), blurUv);
339
+ o = put(out, o, +d.nearBlur, Math.round(Math.min(32, Math.max(8, quality.dofTaps))), depth?.near ?? 0, depth?.far ?? 0);
340
+ if (depth) {
341
+ for (let i = 0; i < 16; i++)
342
+ out[o + i] = depth.invProj[i];
343
+ }
344
+ else {
345
+ for (let i = 0; i < 16; i++)
346
+ out[o + i] = 0;
347
+ }
348
+ o += 16;
349
+ // Finishing passes (ws/post-2).
350
+ const x = s.fxaa;
351
+ const l = s.lut;
352
+ const gr = s.grain;
353
+ const ab = s.aberration;
354
+ const ol = s.outline;
355
+ o = put(out, o, x.enabled && quality.fxaa ? 1 : 0, x.subpixel, x.edgeThreshold, 0);
356
+ const n = Math.max(2, l.size);
357
+ o = put(out, o, l.texture !== null ? 1 : 0, (n - 1) / n, l.intensity, 0.5 / n);
358
+ o = put(out, o, gr.intensity, Math.max(1, gr.size), gr.luminanceResponse, gr.animated ? grainSeed : 0);
359
+ o = put(out, o, ab.intensity, +ab.radialOnly, 0, 0);
360
+ o = put(out, o, s.sharpen.amount, 0, 0, 0);
361
+ const outline = depth !== null && ol.enabled && quality.outline ? 1 : 0;
362
+ // `thickness` is in output px; the outline pass runs at the scene size.
363
+ const thick = depth ? (ol.thickness * width) / Math.max(1, depth.outputWidth) : ol.thickness;
364
+ o = put(out, o, outline, thick, ol.depthThreshold, ol.normalThreshold);
365
+ o = put(out, o, ol.color[0], ol.color[1], ol.color[2], ol.intensity);
366
+ // Post 3 (ws/post-3). The temporal passes need depth and a frame state (a view matrix).
367
+ const temporal = depth !== null && frame !== null;
368
+ const t = s.taa;
369
+ const taa = temporal && t.enabled && quality.taa ? 1 : 0;
370
+ o = put(out, o, taa, Math.min(0.98, Math.max(0, t.feedback)), Math.max(0, t.sharpen), t.clampMode === 'variance' ? 1 : 0);
371
+ o = put(out, o, frame?.jitterX ?? 0, frame?.jitterY ?? 0, frame?.reset ?? 1, taa ? (frame?.taaVelocity ?? 0) : 0);
372
+ const sr = s.ssr;
373
+ const ssr = depth !== null && sr.enabled && quality.ssr ? 1 : 0;
374
+ o = put(out, o, ssr, Math.round(Math.min(64, Math.max(8, sr.maxSteps))), Math.max(1e-3, sr.thickness), Math.max(1e-3, sr.maxDistance));
375
+ o = put(out, o, sr.roughnessCutoff, sr.intensity, Math.max(0, sr.fadeEdges), ssr ? (frame?.roughMetal ?? 0) : 0);
376
+ const ex = s.exposure;
377
+ o = put(out, o, auto ? 1 : 0, ex.histogramLow, ex.histogramHigh, ex.keyValue);
378
+ const wb = s.whiteBalance;
379
+ const wbOn = wb.temperature !== 6500 || wb.tint !== 0;
380
+ if (wbOn) {
381
+ whiteBalanceMatrix(WB, wb.temperature, wb.tint);
382
+ o = put(out, o, WB[0], WB[1], WB[2], 1);
383
+ o = put(out, o, WB[3], WB[4], WB[5], 0);
384
+ o = put(out, o, WB[6], WB[7], WB[8], 0);
385
+ }
386
+ else {
387
+ o = put(out, o, 1, 0, 0, 0);
388
+ o = put(out, o, 0, 1, 0, 0);
389
+ o = put(out, o, 0, 0, 1, 0);
390
+ }
391
+ const mb = s.motionBlur;
392
+ const mblur = temporal && mb.enabled && quality.motionBlur ? 1 : 0;
393
+ o = put(out, o, mblur, mb.intensity, Math.round(Math.min(16, Math.max(2, mb.maxSamples))), mblur ? (frame?.objectVelocity ?? 0) : 0);
394
+ const dirt = b.dirt;
395
+ o = put(out, o, dirt && dirt.texture !== null ? 1 : 0, dirt?.intensity ?? 0, 0, 0);
396
+ if (frame) {
397
+ for (let i = 0; i < 16; i++)
398
+ out[o + i] = frame.reproj[i];
399
+ }
400
+ else {
401
+ for (let i = 0; i < 16; i++)
402
+ out[o + i] = IDENTITY[i];
403
+ }
404
+ o += 16;
405
+ put(out, o, Math.max(0, t.depthReject), Math.max(0, t.velocityReject), t.historyFilter === 'bilinear' ? 0 : 1, 0);
406
+ }
407
+ const DEFAULT_QUALITY = defaultPostQuality();
408
+ const WB = new Float32Array(9);
409
+ const IDENTITY = mat4.create();
410
+ function put(out, o, x, y, z, w) {
411
+ out[o] = x;
412
+ out[o + 1] = y;
413
+ out[o + 2] = z;
414
+ out[o + 3] = w;
415
+ return o + 4;
416
+ }
@@ -0,0 +1,25 @@
1
+ /** Bright-pass + 2× downsample into bloom mip 0. Karis-weighted to tame fireflies. */
2
+ export declare const PREFILTER_WGSL = "\n\nstruct PostUniforms {\n frame: vec4f, // time, aspect, 1/width, 1/height\n enables: vec4f, // bloom, fog, rays, vignette\n enables2: vec4f, // grade, tonemap (0 none, 1 aces, 2 reinhard), dither, exposure\n bloom: vec4f, // intensity, threshold, knee, emissiveWeight\n bloom2: vec4f, // scatter, -, -, -\n fogColor: vec4f, // rgb, density\n fog: vec4f, // top, bottom, noiseScale, speed\n fog2: vec4f, // lightScatter, falloff, -, -\n rays: vec4f, // light uv x, y, intensity, decay\n rays2: vec4f, // density, weight, source radius (uv-y units), source intensity\n raysColor: vec4f, // rgb, bloomSource\n vignette: vec4f, // intensity, radius, softness, -\n vignetteColor: vec4f,\n lift: vec4f,\n gamma: vec4f,\n gain: vec4f,\n grade: vec4f, // saturation, contrast, -, -\n quality: vec4f, // ray samples, fog octaves, ortho projection (1, ws/post-4), -\n ao: vec4f, // enabled (0 without depth), radius, intensity, bias\n ao2: vec4f, // power, samples, proj x scale, proj y scale\n dof: vec4f, // enabled (0 without depth), focus distance, focus range, max blur (uv of width)\n dof2: vec4f, // near blur, taps, near plane, far plane\n invProj: mat4x4f, // clip -> view\n fxaa: vec4f, // enabled, subpixel, edgeThreshold, -\n lut: vec4f, // enabled, (size-1)/size, intensity, 0.5/size\n grain: vec4f, // intensity, cell size (px), luminanceResponse, seed\n aberration: vec4f, // intensity (px), radialOnly, -, -\n sharpen: vec4f, // amount, -, -, -\n outline: vec4f, // enabled (0 without depth), thickness (scene px), depthThreshold, normalThreshold\n outlineColor: vec4f, // rgb, intensity\n // Post 3 (ws/post-3).\n taa: vec4f, // enabled (0 without depth), feedback, sharpen, clampMode (0 aabb, 1 variance)\n taa2: vec4f, // view jitter x (uv), view jitter y (uv), reset (1 = copy the current frame), velocity reprojection (1, ws/post-5)\n ssr: vec4f, // enabled (0 without depth), maxSteps, thickness, maxDistance\n ssr2: vec4f, // roughnessCutoff, intensity, fadeEdges (uv), rough-metal target (1)\n exposure: vec4f, // auto (1), histogramLow, histogramHigh, keyValue\n wb0: vec4f, // white-balance matrix row 0 (rgb weights), enabled\n wb1: vec4f, // row 1\n wb2: vec4f, // row 2\n motionBlur: vec4f, // enabled (0 without depth), intensity, samples, velocity target (1 = object)\n dirt: vec4f, // enabled, intensity, -, -\n reproj: mat4x4f, // current clip -> previous frame's clip (identity on a reset)\n // Post 5 (ws/post-5).\n taa3: vec4f, // depthReject (fraction), velocityReject (px), history filter (1 = Catmull-Rom, ws/post-6), -\n};\n@group(0) @binding(0) var<uniform> P: PostUniforms;\n\n@group(0) @binding(1) var samp: sampler;\n@group(0) @binding(2) var colorTex: texture_2d<f32>;\n@group(0) @binding(3) var emissiveTex: texture_2d<f32>;\n\nfn soft_threshold(c: vec3f) -> vec3f {\n let br = max(c.r, max(c.g, c.b));\n let knee = max(P.bloom.z, 1e-4);\n var rq = clamp(br - P.bloom.y + knee, 0.0, 2.0 * knee);\n rq = rq * rq / (4.0 * knee);\n let w = max(rq, br - P.bloom.y) / max(br, 1e-4);\n return c * w;\n}\n\nfn tap(uv: vec2f) -> vec3f {\n let c = textureSampleLevel(colorTex, samp, uv, 0.0).rgb;\n let e = textureSampleLevel(emissiveTex, samp, uv, 0.0).rgb;\n return min(e * P.bloom.w + soft_threshold(c), vec3f(64.0));\n}\n\n@fragment fn fs_main(in: FullscreenIn) -> @location(0) vec4f {\n let t = 1.0 / vec2f(textureDimensions(colorTex));\n let a = tap(in.uv + t * vec2f(-1.0, -1.0));\n let b = tap(in.uv + t * vec2f(1.0, -1.0));\n let c = tap(in.uv + t * vec2f(-1.0, 1.0));\n let d = tap(in.uv + t * vec2f(1.0, 1.0));\n let wa = 1.0 / (1.0 + engine_luma(a));\n let wb = 1.0 / (1.0 + engine_luma(b));\n let wc = 1.0 / (1.0 + engine_luma(c));\n let wd = 1.0 / (1.0 + engine_luma(d));\n let sum = (a * wa + b * wb + c * wc + d * wd) / max(wa + wb + wc + wd, 1e-4);\n return vec4f(sum, 1.0);\n}\n";
3
+ /** 13-tap downsample (Jimenez 2014). */
4
+ export declare const DOWN_WGSL = "\n@group(0) @binding(1) var samp: sampler;\n@group(0) @binding(2) var src: texture_2d<f32>;\n\nfn s(uv: vec2f) -> vec3f { return textureSampleLevel(src, samp, uv, 0.0).rgb; }\n\n@fragment fn fs_main(in: FullscreenIn) -> @location(0) vec4f {\n let t = 1.0 / vec2f(textureDimensions(src));\n let uv = in.uv;\n let a = s(uv + t * vec2f(-2.0, -2.0));\n let b = s(uv + t * vec2f(0.0, -2.0));\n let c = s(uv + t * vec2f(2.0, -2.0));\n let d = s(uv + t * vec2f(-1.0, -1.0));\n let e = s(uv + t * vec2f(1.0, -1.0));\n let f = s(uv + t * vec2f(-2.0, 0.0));\n let g = s(uv);\n let h = s(uv + t * vec2f(2.0, 0.0));\n let i = s(uv + t * vec2f(-1.0, 1.0));\n let j = s(uv + t * vec2f(1.0, 1.0));\n let k = s(uv + t * vec2f(-2.0, 2.0));\n let l = s(uv + t * vec2f(0.0, 2.0));\n let m = s(uv + t * vec2f(2.0, 2.0));\n var o = (d + e + i + j) * 0.125;\n o += (a + b + g + f) * 0.03125;\n o += (b + c + h + g) * 0.03125;\n o += (f + g + l + k) * 0.03125;\n o += (g + h + m + l) * 0.03125;\n return vec4f(o, 1.0);\n}\n";
5
+ /** 9-tap tent upsample; additively blended (× blend constant) onto the finer level. */
6
+ export declare const UP_WGSL = "\n@group(0) @binding(1) var samp: sampler;\n@group(0) @binding(2) var src: texture_2d<f32>;\n\nfn s(uv: vec2f) -> vec3f { return textureSampleLevel(src, samp, uv, 0.0).rgb; }\n\n@fragment fn fs_main(in: FullscreenIn) -> @location(0) vec4f {\n let t = 1.0 / vec2f(textureDimensions(src));\n let uv = in.uv;\n var o = s(uv) * 4.0;\n o += (s(uv + vec2f(-t.x, 0.0)) + s(uv + vec2f(t.x, 0.0)) + s(uv + vec2f(0.0, -t.y)) + s(uv + vec2f(0.0, t.y))) * 2.0;\n o += s(uv + vec2f(-t.x, -t.y)) + s(uv + vec2f(t.x, -t.y)) + s(uv + vec2f(-t.x, t.y)) + s(uv + vec2f(t.x, t.y));\n return vec4f(o / 16.0, 1.0);\n}\n";
7
+ /** Screen-space god rays: radial march towards the light over bloom + a light disc. */
8
+ export declare const RAYS_WGSL = "\n\nstruct PostUniforms {\n frame: vec4f, // time, aspect, 1/width, 1/height\n enables: vec4f, // bloom, fog, rays, vignette\n enables2: vec4f, // grade, tonemap (0 none, 1 aces, 2 reinhard), dither, exposure\n bloom: vec4f, // intensity, threshold, knee, emissiveWeight\n bloom2: vec4f, // scatter, -, -, -\n fogColor: vec4f, // rgb, density\n fog: vec4f, // top, bottom, noiseScale, speed\n fog2: vec4f, // lightScatter, falloff, -, -\n rays: vec4f, // light uv x, y, intensity, decay\n rays2: vec4f, // density, weight, source radius (uv-y units), source intensity\n raysColor: vec4f, // rgb, bloomSource\n vignette: vec4f, // intensity, radius, softness, -\n vignetteColor: vec4f,\n lift: vec4f,\n gamma: vec4f,\n gain: vec4f,\n grade: vec4f, // saturation, contrast, -, -\n quality: vec4f, // ray samples, fog octaves, ortho projection (1, ws/post-4), -\n ao: vec4f, // enabled (0 without depth), radius, intensity, bias\n ao2: vec4f, // power, samples, proj x scale, proj y scale\n dof: vec4f, // enabled (0 without depth), focus distance, focus range, max blur (uv of width)\n dof2: vec4f, // near blur, taps, near plane, far plane\n invProj: mat4x4f, // clip -> view\n fxaa: vec4f, // enabled, subpixel, edgeThreshold, -\n lut: vec4f, // enabled, (size-1)/size, intensity, 0.5/size\n grain: vec4f, // intensity, cell size (px), luminanceResponse, seed\n aberration: vec4f, // intensity (px), radialOnly, -, -\n sharpen: vec4f, // amount, -, -, -\n outline: vec4f, // enabled (0 without depth), thickness (scene px), depthThreshold, normalThreshold\n outlineColor: vec4f, // rgb, intensity\n // Post 3 (ws/post-3).\n taa: vec4f, // enabled (0 without depth), feedback, sharpen, clampMode (0 aabb, 1 variance)\n taa2: vec4f, // view jitter x (uv), view jitter y (uv), reset (1 = copy the current frame), velocity reprojection (1, ws/post-5)\n ssr: vec4f, // enabled (0 without depth), maxSteps, thickness, maxDistance\n ssr2: vec4f, // roughnessCutoff, intensity, fadeEdges (uv), rough-metal target (1)\n exposure: vec4f, // auto (1), histogramLow, histogramHigh, keyValue\n wb0: vec4f, // white-balance matrix row 0 (rgb weights), enabled\n wb1: vec4f, // row 1\n wb2: vec4f, // row 2\n motionBlur: vec4f, // enabled (0 without depth), intensity, samples, velocity target (1 = object)\n dirt: vec4f, // enabled, intensity, -, -\n reproj: mat4x4f, // current clip -> previous frame's clip (identity on a reset)\n // Post 5 (ws/post-5).\n taa3: vec4f, // depthReject (fraction), velocityReject (px), history filter (1 = Catmull-Rom, ws/post-6), -\n};\n@group(0) @binding(0) var<uniform> P: PostUniforms;\n\n@group(0) @binding(1) var samp: sampler;\n@group(0) @binding(2) var bloomTex: texture_2d<f32>;\n\n// Reference sample count: decay and weight are tuned per step at 64 samples. With fewer\n// samples (P.quality.x) each step covers more distance, so decay and weight are rescaled.\nconst REF_SAMPLES: f32 = 64.0;\n\nfn source(uv: vec2f) -> vec3f {\n let aspect = P.frame.y;\n var s = textureSampleLevel(bloomTex, samp, uv, 0.0).rgb * P.raysColor.w;\n let d = length((uv - P.rays.xy) * vec2f(aspect, 1.0));\n let r = max(P.rays2.z, 1e-4);\n let disc = 1.0 - smoothstep(0.0, r, d);\n s += vec3f(disc * disc * P.rays2.w);\n // Outside the screen there is no bloom; keep the disc (light can sit off-screen).\n return s * P.raysColor.rgb;\n}\n\n@fragment fn fs_main(in: FullscreenIn) -> @location(0) vec4f {\n var coord = in.uv;\n // Small per-pixel jitter hides the step pattern.\n let jitter = engine_hash21(in.pos.xy + fract(P.frame.x) * 61.0);\n let n = i32(clamp(P.quality.x, 8.0, 64.0));\n let k = REF_SAMPLES / f32(n);\n let delta = (coord - P.rays.xy) * (P.rays2.x / f32(n));\n coord -= delta * jitter;\n let decay = pow(P.rays.w, k);\n let weight = P.rays2.y * k;\n var illum = 1.0;\n var acc = vec3f(0.0);\n for (var i = 0; i < n; i++) {\n coord -= delta;\n acc += source(coord) * illum * weight;\n illum *= decay;\n }\n return vec4f(acc * P.rays.z, 1.0);\n}\n";
9
+ /** Final pass: combine, SSR, mist, bloom (× dirt), rays, white balance, exposure, tonemap, grade, vignette, dither, sRGB. */
10
+ export declare const COMPOSITE_WGSL = "\n\nstruct PostUniforms {\n frame: vec4f, // time, aspect, 1/width, 1/height\n enables: vec4f, // bloom, fog, rays, vignette\n enables2: vec4f, // grade, tonemap (0 none, 1 aces, 2 reinhard), dither, exposure\n bloom: vec4f, // intensity, threshold, knee, emissiveWeight\n bloom2: vec4f, // scatter, -, -, -\n fogColor: vec4f, // rgb, density\n fog: vec4f, // top, bottom, noiseScale, speed\n fog2: vec4f, // lightScatter, falloff, -, -\n rays: vec4f, // light uv x, y, intensity, decay\n rays2: vec4f, // density, weight, source radius (uv-y units), source intensity\n raysColor: vec4f, // rgb, bloomSource\n vignette: vec4f, // intensity, radius, softness, -\n vignetteColor: vec4f,\n lift: vec4f,\n gamma: vec4f,\n gain: vec4f,\n grade: vec4f, // saturation, contrast, -, -\n quality: vec4f, // ray samples, fog octaves, ortho projection (1, ws/post-4), -\n ao: vec4f, // enabled (0 without depth), radius, intensity, bias\n ao2: vec4f, // power, samples, proj x scale, proj y scale\n dof: vec4f, // enabled (0 without depth), focus distance, focus range, max blur (uv of width)\n dof2: vec4f, // near blur, taps, near plane, far plane\n invProj: mat4x4f, // clip -> view\n fxaa: vec4f, // enabled, subpixel, edgeThreshold, -\n lut: vec4f, // enabled, (size-1)/size, intensity, 0.5/size\n grain: vec4f, // intensity, cell size (px), luminanceResponse, seed\n aberration: vec4f, // intensity (px), radialOnly, -, -\n sharpen: vec4f, // amount, -, -, -\n outline: vec4f, // enabled (0 without depth), thickness (scene px), depthThreshold, normalThreshold\n outlineColor: vec4f, // rgb, intensity\n // Post 3 (ws/post-3).\n taa: vec4f, // enabled (0 without depth), feedback, sharpen, clampMode (0 aabb, 1 variance)\n taa2: vec4f, // view jitter x (uv), view jitter y (uv), reset (1 = copy the current frame), velocity reprojection (1, ws/post-5)\n ssr: vec4f, // enabled (0 without depth), maxSteps, thickness, maxDistance\n ssr2: vec4f, // roughnessCutoff, intensity, fadeEdges (uv), rough-metal target (1)\n exposure: vec4f, // auto (1), histogramLow, histogramHigh, keyValue\n wb0: vec4f, // white-balance matrix row 0 (rgb weights), enabled\n wb1: vec4f, // row 1\n wb2: vec4f, // row 2\n motionBlur: vec4f, // enabled (0 without depth), intensity, samples, velocity target (1 = object)\n dirt: vec4f, // enabled, intensity, -, -\n reproj: mat4x4f, // current clip -> previous frame's clip (identity on a reset)\n // Post 5 (ws/post-5).\n taa3: vec4f, // depthReject (fraction), velocityReject (px), history filter (1 = Catmull-Rom, ws/post-6), -\n};\n@group(0) @binding(0) var<uniform> P: PostUniforms;\n\n@group(0) @binding(1) var samp: sampler;\n@group(0) @binding(2) var colorTex: texture_2d<f32>;\n@group(0) @binding(3) var emissiveTex: texture_2d<f32>;\n@group(0) @binding(4) var bloomTex: texture_2d<f32>;\n@group(0) @binding(5) var raysTex: texture_2d<f32>;\n// Depth passes (ws/post-depth); bound to a placeholder and ignored while P.ao.x / P.dof.x are 0.\n@group(0) @binding(6) var aoTex: texture_2d<f32>;\n@group(0) @binding(7) var dofTex: texture_2d<f32>;\n// Finishing (ws/post-2): the outline mask (placeholder while P.outline.x is 0) and the 3D LUT\n// (a 1\u00D71\u00D71 placeholder while P.lut.x is 0).\n@group(0) @binding(8) var outlineTex: texture_2d<f32>;\n@group(0) @binding(9) var lutTex: texture_3d<f32>;\n// Post 3 (ws/post-3): the half-res SSR term (placeholder while P.ssr.x is 0) and the lens-dirt\n// map (placeholder while P.dirt.x is 0). TAA and motion blur replace colorTex with their result.\n@group(0) @binding(10) var ssrTex: texture_2d<f32>;\n@group(0) @binding(11) var dirtTex: texture_2d<f32>;\n\n/** The scene term: colour \u00D7 AO, blended toward the DoF result by CoC, plus emissive. */\nfn scene_at(uv: vec2f) -> vec3f {\n var scene = textureSampleLevel(colorTex, samp, uv, 0.0).rgb;\n if (P.ao.x > 0.5) {\n scene *= textureSampleLevel(aoTex, samp, uv, 0.0).r;\n }\n if (P.dof.x > 0.5) {\n // Alpha holds the signed CoC in uv of the width; blend in the blur from half a scene px up.\n let blur = textureSampleLevel(dofTex, samp, uv, 0.0);\n let px = abs(blur.a) / P.frame.z;\n scene = mix(scene, blur.rgb, smoothstep(0.5, 2.0, px));\n }\n if (P.ssr.x > 0.5) {\n // Premultiplied by its fade and intensity in the SSR pass.\n scene += textureSampleLevel(ssrTex, samp, uv, 0.0).rgb;\n }\n return scene + textureSampleLevel(emissiveTex, samp, uv, 0.0).rgb;\n}\n\nfn aces(x: vec3f) -> vec3f {\n let a = 2.51;\n let b = 0.03;\n let c = 2.43;\n let d = 0.59;\n let e = 0.14;\n return clamp((x * (a * x + b)) / (x * (c * x + d) + e), vec3f(0.0), vec3f(1.0));\n}\n\nfn to_srgb(c: vec3f) -> vec3f {\n let lo = c * 12.92;\n let hi = 1.055 * pow(c, vec3f(1.0 / 2.4)) - 0.055;\n return select(hi, lo, c <= vec3f(0.0031308));\n}\n\n@fragment fn fs_main(in: FullscreenIn) -> @location(0) vec4f {\n let uv = in.uv;\n let aspect = P.frame.y;\n let time = P.frame.x;\n let texel = vec2f(P.frame.z, P.frame.w);\n var c: vec3f;\n if (P.aberration.x > 0.0) {\n // Red and blue sampled either side of green along the radius, in scene px.\n let d = (uv - 0.5) * vec2f(aspect, 1.0);\n let r = length(d) / length(vec2f(aspect, 1.0) * 0.5);\n let dir = d / max(length(d), 1e-4);\n let amount = P.aberration.x * select(1.0, r * r, P.aberration.y > 0.5);\n let o = dir * amount * texel;\n c = vec3f(scene_at(uv + o).r, scene_at(uv).g, scene_at(uv - o).b);\n } else {\n c = scene_at(uv);\n }\n if (P.sharpen.x > 0.0) {\n // Unsharp mask: the pixel minus its 4-neighbour mean, clamped so dark rims never go negative.\n let blur = (scene_at(uv + vec2f(texel.x, 0.0)) + scene_at(uv - vec2f(texel.x, 0.0))\n + scene_at(uv + vec2f(0.0, texel.y)) + scene_at(uv - vec2f(0.0, texel.y))) * 0.25;\n c = max(c + (c - blur) * P.sharpen.x, vec3f(0.0));\n }\n if (P.outline.x > 0.5) {\n let edge = textureSampleLevel(outlineTex, samp, uv, 0.0).r;\n c = mix(c, P.outlineColor.rgb, clamp(edge * P.outlineColor.w, 0.0, 1.0));\n }\n\n var bloom = vec3f(0.0);\n if (P.enables.x > 0.5) {\n bloom = textureSampleLevel(bloomTex, samp, uv, 0.0).rgb;\n if (P.dirt.x > 0.5) {\n bloom *= 1.0 + textureSampleLevel(dirtTex, samp, uv, 0.0).rgb * P.dirt.y;\n }\n }\n\n // Floor mist: height mask \u00D7 two drifting fbm layers, lit by nearby glow.\n if (P.enables.y > 0.5) {\n let span = max(P.fog.y - P.fog.x, 1e-3);\n let h = pow(clamp((uv.y - P.fog.x) / span, 0.0, 1.0), P.fog2.y);\n if (h > 0.0) {\n let p = vec2f(uv.x * aspect, uv.y * 2.2) * P.fog.z;\n let t = time * P.fog.w;\n // Fewer octaves (P.quality.y < 5) lose fine wisps; add back the dropped octaves' mean\n // (0.5 each) so the mist keeps its density.\n let o1 = i32(clamp(P.quality.y, 2.0, 5.0));\n let o2 = max(o1 - 1, 1);\n let m1 = 0.5 * (pow(0.5, f32(o1)) - pow(0.5, 5.0));\n let m2 = 0.5 * (pow(0.5, f32(o2)) - pow(0.5, 4.0));\n let n1 = engine_fbm(p + vec2f(t * 6.0, t * 0.7), o1) + m1;\n let n2 = engine_fbm(p * 1.9 + vec2f(-t * 9.0, t * 1.3) + vec2f(13.7, 4.1), o2) + m2;\n let wisps = smoothstep(0.2, 0.85, n1 * 0.6 + n2 * 0.55);\n let amount = clamp(h * P.fogColor.w * (0.3 + 1.1 * wisps), 0.0, 1.0);\n let lit = P.fogColor.rgb * (0.6 + 0.8 * wisps) + bloom * P.fog2.x;\n c = mix(c, lit, amount);\n }\n }\n\n c += bloom * P.bloom.x;\n if (P.enables.z > 0.5) {\n c += textureSampleLevel(raysTex, samp, uv, 0.0).rgb;\n }\n\n if (P.wb0.w > 0.5) {\n // White balance: a 3\u00D73 matrix on the linear colour (ws/post-3).\n c = max(vec3f(dot(P.wb0.rgb, c), dot(P.wb1.rgb, c), dot(P.wb2.rgb, c)), vec3f(0.0));\n }\n c *= P.enables2.w;\n let tm = P.enables2.y;\n if (tm > 0.5 && tm < 1.5) {\n c = aces(c);\n } else if (tm > 1.5) {\n c = c / (1.0 + c);\n }\n c = clamp(c, vec3f(0.0), vec3f(1.0));\n\n if (P.enables2.x > 0.5) {\n c = c * P.gain.rgb + P.lift.rgb * (1.0 - c);\n c = pow(max(c, vec3f(0.0)), 1.0 / max(P.gamma.rgb, vec3f(1e-3)));\n let l = engine_luma(c);\n c = mix(vec3f(l), c, P.grade.x);\n c = (c - 0.5) * P.grade.y + 0.5;\n c = clamp(c, vec3f(0.0), vec3f(1.0));\n }\n\n if (P.lut.x > 0.5) {\n // Texel centres: the LUT's first and last texels sit half a texel in from 0 and 1.\n let looked = textureSampleLevel(lutTex, samp, c * P.lut.y + P.lut.w, 0.0).rgb;\n c = clamp(mix(c, looked, P.lut.z), vec3f(0.0), vec3f(1.0));\n }\n if (P.grain.x > 0.0) {\n let cell = floor(in.pos.xy / P.grain.y);\n let n = engine_hash21(cell + vec2f(P.grain.w * 61.0, P.grain.w * 29.0)) - 0.5;\n let response = 1.0 - P.grain.z * engine_luma(c);\n c = clamp(c + n * P.grain.x * response, vec3f(0.0), vec3f(1.0));\n }\n\n if (P.enables.w > 0.5) {\n let d = length((uv - 0.5) * vec2f(aspect, 1.0)) / length(vec2f(aspect, 1.0) * 0.5);\n let v = smoothstep(P.vignette.y, P.vignette.y + P.vignette.z, d) * P.vignette.x;\n c = mix(c, P.vignetteColor.rgb, v);\n }\n\n var outc = to_srgb(c);\n if (P.enables2.z > 0.5) {\n outc += (engine_hash21(in.pos.xy) - 0.5) / 255.0;\n }\n return vec4f(outc, 1.0);\n}\n";
11
+ /**
12
+ * FXAA 3.11, console preset (ws/post-2): one full-res pass over the composite's display-encoded
13
+ * output. Luma from the four texel-corner taps and the centre; the blend direction from their
14
+ * gradient, two taps half a px out and two more up to two px out along it, the longer pair
15
+ * dropped when its luma leaves the local range. `P.fxaa.y` (subpixel) scales the blend;
16
+ * `P.fxaa.z` (edgeThreshold) is the relative contrast below which the pixel is left alone.
17
+ */
18
+ export declare const FXAA_WGSL = "\n\nstruct PostUniforms {\n frame: vec4f, // time, aspect, 1/width, 1/height\n enables: vec4f, // bloom, fog, rays, vignette\n enables2: vec4f, // grade, tonemap (0 none, 1 aces, 2 reinhard), dither, exposure\n bloom: vec4f, // intensity, threshold, knee, emissiveWeight\n bloom2: vec4f, // scatter, -, -, -\n fogColor: vec4f, // rgb, density\n fog: vec4f, // top, bottom, noiseScale, speed\n fog2: vec4f, // lightScatter, falloff, -, -\n rays: vec4f, // light uv x, y, intensity, decay\n rays2: vec4f, // density, weight, source radius (uv-y units), source intensity\n raysColor: vec4f, // rgb, bloomSource\n vignette: vec4f, // intensity, radius, softness, -\n vignetteColor: vec4f,\n lift: vec4f,\n gamma: vec4f,\n gain: vec4f,\n grade: vec4f, // saturation, contrast, -, -\n quality: vec4f, // ray samples, fog octaves, ortho projection (1, ws/post-4), -\n ao: vec4f, // enabled (0 without depth), radius, intensity, bias\n ao2: vec4f, // power, samples, proj x scale, proj y scale\n dof: vec4f, // enabled (0 without depth), focus distance, focus range, max blur (uv of width)\n dof2: vec4f, // near blur, taps, near plane, far plane\n invProj: mat4x4f, // clip -> view\n fxaa: vec4f, // enabled, subpixel, edgeThreshold, -\n lut: vec4f, // enabled, (size-1)/size, intensity, 0.5/size\n grain: vec4f, // intensity, cell size (px), luminanceResponse, seed\n aberration: vec4f, // intensity (px), radialOnly, -, -\n sharpen: vec4f, // amount, -, -, -\n outline: vec4f, // enabled (0 without depth), thickness (scene px), depthThreshold, normalThreshold\n outlineColor: vec4f, // rgb, intensity\n // Post 3 (ws/post-3).\n taa: vec4f, // enabled (0 without depth), feedback, sharpen, clampMode (0 aabb, 1 variance)\n taa2: vec4f, // view jitter x (uv), view jitter y (uv), reset (1 = copy the current frame), velocity reprojection (1, ws/post-5)\n ssr: vec4f, // enabled (0 without depth), maxSteps, thickness, maxDistance\n ssr2: vec4f, // roughnessCutoff, intensity, fadeEdges (uv), rough-metal target (1)\n exposure: vec4f, // auto (1), histogramLow, histogramHigh, keyValue\n wb0: vec4f, // white-balance matrix row 0 (rgb weights), enabled\n wb1: vec4f, // row 1\n wb2: vec4f, // row 2\n motionBlur: vec4f, // enabled (0 without depth), intensity, samples, velocity target (1 = object)\n dirt: vec4f, // enabled, intensity, -, -\n reproj: mat4x4f, // current clip -> previous frame's clip (identity on a reset)\n // Post 5 (ws/post-5).\n taa3: vec4f, // depthReject (fraction), velocityReject (px), history filter (1 = Catmull-Rom, ws/post-6), -\n};\n@group(0) @binding(0) var<uniform> P: PostUniforms;\n\n@group(0) @binding(1) var samp: sampler;\n@group(0) @binding(2) var src: texture_2d<f32>;\n\nconst EDGE_SHARPNESS: f32 = 8.0;\nconst EDGE_THRESHOLD_MIN: f32 = 0.05;\n\nfn tap(uv: vec2f) -> vec3f { return textureSampleLevel(src, samp, uv, 0.0).rgb; }\n\n@fragment fn fs_main(in: FullscreenIn) -> @location(0) vec4f {\n let t = 1.0 / vec2f(textureDimensions(src));\n let uv = in.uv;\n let rgbM = tap(uv);\n let lumaM = engine_luma(rgbM);\n let lumaNw = engine_luma(tap(uv + vec2f(-0.5, -0.5) * t)) + 1.0 / 384.0;\n let lumaNe = engine_luma(tap(uv + vec2f(0.5, -0.5) * t));\n let lumaSw = engine_luma(tap(uv + vec2f(-0.5, 0.5) * t));\n let lumaSe = engine_luma(tap(uv + vec2f(0.5, 0.5) * t));\n let lumaMax = max(max(lumaNw, lumaNe), max(lumaSw, lumaSe));\n let lumaMin = min(min(lumaNw, lumaNe), min(lumaSw, lumaSe));\n let range = max(lumaMax, lumaM) - min(lumaMin, lumaM);\n if (range < max(EDGE_THRESHOLD_MIN, lumaMax * P.fxaa.z)) {\n return vec4f(rgbM, 1.0);\n }\n var dir = vec2f(-((lumaNw + lumaNe) - (lumaSw + lumaSe)), (lumaNw + lumaSw) - (lumaNe + lumaSe));\n let dir1 = dir / max(length(dir), 1e-5);\n let rgbN1 = tap(uv - dir1 * 0.5 * t);\n let rgbP1 = tap(uv + dir1 * 0.5 * t);\n let dirAbsMinTimesC = min(abs(dir1.x), abs(dir1.y)) * EDGE_SHARPNESS;\n let dir2 = clamp(dir1 / max(dirAbsMinTimesC, 1e-5), vec2f(-2.0), vec2f(2.0));\n let rgbN2 = tap(uv - dir2 * 2.0 * t);\n let rgbP2 = tap(uv + dir2 * 2.0 * t);\n let rgbA = (rgbN1 + rgbP1) * 0.5;\n let rgbB = (rgbN2 + rgbP2) * 0.25 + rgbA * 0.5;\n let lumaB = engine_luma(rgbB);\n let outside = lumaB < lumaMin || lumaB > lumaMax;\n let aa = select(rgbB, rgbA, outside);\n return vec4f(mix(rgbM, aa, clamp(P.fxaa.y, 0.0, 1.0)), 1.0);\n}\n";
19
+ /**
20
+ * Outline mask (ws/post-2): a Roberts cross over the linear depth and the normals from depth,
21
+ * at the scene size, written as an edge strength 0..1 (r8unorm) the composite mixes toward
22
+ * `outline.color` before fog. The cross is sampled `P.outline.y` scene px from the pixel.
23
+ * Sky (depth 1) counts as very far, so silhouettes against it are edges.
24
+ */
25
+ export declare const OUTLINE_WGSL = "\n\nstruct PostUniforms {\n frame: vec4f, // time, aspect, 1/width, 1/height\n enables: vec4f, // bloom, fog, rays, vignette\n enables2: vec4f, // grade, tonemap (0 none, 1 aces, 2 reinhard), dither, exposure\n bloom: vec4f, // intensity, threshold, knee, emissiveWeight\n bloom2: vec4f, // scatter, -, -, -\n fogColor: vec4f, // rgb, density\n fog: vec4f, // top, bottom, noiseScale, speed\n fog2: vec4f, // lightScatter, falloff, -, -\n rays: vec4f, // light uv x, y, intensity, decay\n rays2: vec4f, // density, weight, source radius (uv-y units), source intensity\n raysColor: vec4f, // rgb, bloomSource\n vignette: vec4f, // intensity, radius, softness, -\n vignetteColor: vec4f,\n lift: vec4f,\n gamma: vec4f,\n gain: vec4f,\n grade: vec4f, // saturation, contrast, -, -\n quality: vec4f, // ray samples, fog octaves, ortho projection (1, ws/post-4), -\n ao: vec4f, // enabled (0 without depth), radius, intensity, bias\n ao2: vec4f, // power, samples, proj x scale, proj y scale\n dof: vec4f, // enabled (0 without depth), focus distance, focus range, max blur (uv of width)\n dof2: vec4f, // near blur, taps, near plane, far plane\n invProj: mat4x4f, // clip -> view\n fxaa: vec4f, // enabled, subpixel, edgeThreshold, -\n lut: vec4f, // enabled, (size-1)/size, intensity, 0.5/size\n grain: vec4f, // intensity, cell size (px), luminanceResponse, seed\n aberration: vec4f, // intensity (px), radialOnly, -, -\n sharpen: vec4f, // amount, -, -, -\n outline: vec4f, // enabled (0 without depth), thickness (scene px), depthThreshold, normalThreshold\n outlineColor: vec4f, // rgb, intensity\n // Post 3 (ws/post-3).\n taa: vec4f, // enabled (0 without depth), feedback, sharpen, clampMode (0 aabb, 1 variance)\n taa2: vec4f, // view jitter x (uv), view jitter y (uv), reset (1 = copy the current frame), velocity reprojection (1, ws/post-5)\n ssr: vec4f, // enabled (0 without depth), maxSteps, thickness, maxDistance\n ssr2: vec4f, // roughnessCutoff, intensity, fadeEdges (uv), rough-metal target (1)\n exposure: vec4f, // auto (1), histogramLow, histogramHigh, keyValue\n wb0: vec4f, // white-balance matrix row 0 (rgb weights), enabled\n wb1: vec4f, // row 1\n wb2: vec4f, // row 2\n motionBlur: vec4f, // enabled (0 without depth), intensity, samples, velocity target (1 = object)\n dirt: vec4f, // enabled, intensity, -, -\n reproj: mat4x4f, // current clip -> previous frame's clip (identity on a reset)\n // Post 5 (ws/post-5).\n taa3: vec4f, // depthReject (fraction), velocityReject (px), history filter (1 = Catmull-Rom, ws/post-6), -\n};\n@group(0) @binding(0) var<uniform> P: PostUniforms;\n\n@group(0) @binding(1) var nearest: sampler;\n@group(0) @binding(2) var linSamp: sampler;\n@group(0) @binding(3) var depthTex: texture_depth_2d;\n\nfn view_pos(uv: vec2f, depth: f32) -> vec3f {\n let ndc = vec3f(uv.x * 2.0 - 1.0, 1.0 - uv.y * 2.0, depth);\n let p = P.invProj * vec4f(ndc, 1.0);\n return p.xyz / p.w;\n}\nfn linear_depth(uv: vec2f, depth: f32) -> f32 {\n return -view_pos(uv, depth).z;\n}\n/** The centre of the depth texel under `uv`: positions must be rebuilt where the depth was read. */\nfn texel_uv(uv: vec2f, dim: vec2f) -> vec2f {\n return (floor(clamp(uv, vec2f(0.0), vec2f(0.9999)) * dim) + 0.5) / dim;\n}\n\n\nconst SKY_Z: f32 = 1.0e6;\n\n/** View position of the texel under `uv` (w = raw depth). */\nfn fetch(uv: vec2f, dim: vec2f) -> vec4f {\n let tuv = texel_uv(uv, dim);\n let d = textureSampleLevel(depthTex, nearest, tuv, 0u);\n return vec4f(view_pos(tuv, d), d);\n}\n\nfn zof(s: vec4f) -> f32 {\n return select(-s.z, SKY_Z, s.w >= 1.0);\n}\n\n/** Normal at the texel under `uv` from its +x and +y neighbours (a zero vector in the sky). */\nfn normal_at(uv: vec2f, dim: vec2f, t: vec2f) -> vec3f {\n let p = fetch(uv, dim);\n let px = fetch(uv + vec2f(t.x, 0.0), dim);\n let py = fetch(uv + vec2f(0.0, t.y), dim);\n if (p.w >= 1.0 || px.w >= 1.0 || py.w >= 1.0) {\n return vec3f(0.0);\n }\n return normalize(cross(px.xyz - p.xyz, py.xyz - p.xyz));\n}\n\n@fragment fn fs_main(in: FullscreenIn) -> @location(0) vec4f {\n let dim = vec2f(textureDimensions(depthTex));\n let uv = texel_uv(in.uv, dim);\n let t = vec2f(max(P.outline.y, 0.5)) / dim;\n let a = fetch(uv - t, dim);\n let b = fetch(uv + t, dim);\n let c = fetch(uv + vec2f(t.x, -t.y), dim);\n let d = fetch(uv + vec2f(-t.x, t.y), dim);\n let za = zof(a);\n let zb = zof(b);\n let zc = zof(c);\n let zd = zof(d);\n let zmin = min(min(za, zb), min(zc, zd));\n if (zmin >= SKY_Z) {\n return vec4f(0.0, 0.0, 0.0, 1.0);\n }\n // Depth: the cross's two differences relative to the nearest tap (so far edges count less).\n let dz = length(vec2f(za - zb, zc - zd)) / max(zmin, 1e-3);\n let th = max(P.outline.z, 1e-4);\n let edgeDepth = smoothstep(th, th * 2.0, dz);\n // Normals: the cross over the four taps' normals, in 1 - cos.\n let one = 1.0 / dim;\n let na = normal_at(uv - t, dim, one);\n let nb = normal_at(uv + t, dim, one);\n let nc = normal_at(uv + vec2f(t.x, -t.y), dim, one);\n let nd = normal_at(uv + vec2f(-t.x, t.y), dim, one);\n var dn = 0.0;\n if (dot(na, na) > 0.5 && dot(nb, nb) > 0.5) { dn = max(dn, 1.0 - dot(na, nb)); }\n if (dot(nc, nc) > 0.5 && dot(nd, nd) > 0.5) { dn = max(dn, 1.0 - dot(nc, nd)); }\n let tn = max(P.outline.w, 1e-4);\n let edgeNormal = smoothstep(tn, tn * 1.5, dn);\n let edge = max(edgeDepth, edgeNormal);\n return vec4f(edge, edge, edge, 1.0);\n}\n";