@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,41 @@
1
+ /**
2
+ * Colour helpers. The renderer works in **linear** RGB (HDR allowed, values > 1 glow).
3
+ * Author colours as sRGB hex and convert with `hex()`.
4
+ */
5
+ export function srgbToLinear(c) {
6
+ return c <= 0.04045 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4;
7
+ }
8
+ export function linearToSrgb(c) {
9
+ return c <= 0.0031308 ? c * 12.92 : 1.055 * c ** (1 / 2.4) - 0.055;
10
+ }
11
+ /**
12
+ * Parse `#rgb`, `#rrggbb` or `#rrggbbaa` (sRGB) into linear RGBA, optionally scaled by
13
+ * `intensity` (HDR multiplier on RGB, alpha untouched).
14
+ */
15
+ export function hex(value, intensity = 1) {
16
+ let h = value.startsWith('#') ? value.slice(1) : value;
17
+ if (h.length === 3 || h.length === 4) {
18
+ h = h
19
+ .split('')
20
+ .map((ch) => ch + ch)
21
+ .join('');
22
+ }
23
+ const n = (i) => Number.parseInt(h.slice(i, i + 2), 16) / 255;
24
+ if (!/^[0-9a-fA-F]{6}([0-9a-fA-F]{2})?$/.test(h))
25
+ throw new Error(`hex(): bad colour ${value}`);
26
+ const a = h.length === 8 ? n(6) : 1;
27
+ return [
28
+ srgbToLinear(n(0)) * intensity,
29
+ srgbToLinear(n(2)) * intensity,
30
+ srgbToLinear(n(4)) * intensity,
31
+ a,
32
+ ];
33
+ }
34
+ /** Linear RGBA from components (no conversion). */
35
+ export function rgba(r, g, b, a = 1) {
36
+ return [r, g, b, a];
37
+ }
38
+ /** Scale RGB by `k` (alpha untouched). Returns a new array. */
39
+ export function scaleRgb(c, k) {
40
+ return [c[0] * k, c[1] * k, c[2] * k, c[3] ?? 1];
41
+ }
@@ -0,0 +1,104 @@
1
+ /**
2
+ * Block-compressed textures (ws/render-5): which compression families the device has
3
+ * (`RendererCaps`), the features to ask an adapter for, and `uploadKtx2`, which puts a parsed
4
+ * KTX2 file (`@thunderclap-engine/engine/assets` `parseKtx2`) on the GPU in its own format, every mip
5
+ * level written with its block-aligned row pitch. No transcoder: a Basis Universal (BasisLZ /
6
+ * UASTC) or supercompressed payload, or a format the device lacks, throws
7
+ * `CompressedTextureError`.
8
+ */
9
+ import { type Ktx2Texture } from '../assets/ktx2.js';
10
+ import { Texture } from './texture.js';
11
+ /** A block-compression feature family. */
12
+ export type CompressionFamily = 'bc' | 'etc2' | 'astc';
13
+ /** The WebGPU feature of each family. */
14
+ export declare const COMPRESSION_FEATURES: Readonly<Record<CompressionFamily, GPUFeatureName>>;
15
+ /** What the renderer's device can do beyond core WebGPU (`renderer.caps`). */
16
+ export interface RendererCaps {
17
+ /** The compressed texture families the device was created with. */
18
+ readonly compressed: Readonly<Record<CompressionFamily, boolean>>;
19
+ }
20
+ /** Anything with a feature set: a `GPUAdapter` or a `GPUDevice`. */
21
+ interface HasFeatures {
22
+ readonly features?: {
23
+ has(name: string): boolean;
24
+ };
25
+ }
26
+ /**
27
+ * The compression features `adapter` offers, to spread into `requestDevice`'s
28
+ * `requiredFeatures` (a device only gets the features it asks for):
29
+ * `adapter.requestDevice({ requiredFeatures: [...compressionFeatures(adapter)] })`.
30
+ */
31
+ export declare function compressionFeatures(adapter: HasFeatures): GPUFeatureName[];
32
+ /** The caps of `device`, read from its features (none on a device without a feature set). */
33
+ export declare function rendererCaps(device: HasFeatures): RendererCaps;
34
+ /** Why `uploadKtx2` could not upload a file: names the format and the device's caps. */
35
+ export declare class CompressedTextureError extends Error {
36
+ /** The file's WebGPU format, or `vk:<VkFormat>` when WebGPU has none. */
37
+ readonly format: string;
38
+ /** The family the format needs, or null (no WebGPU twin, or a payload to transcode). */
39
+ readonly family: CompressionFamily | null;
40
+ readonly caps: RendererCaps;
41
+ constructor(message: string, format: string, family: CompressionFamily | null, caps: RendererCaps);
42
+ }
43
+ /** Block footprint of a format: texels per block in x and y, and bytes per block. */
44
+ export interface BlockInfo {
45
+ width: number;
46
+ height: number;
47
+ bytes: number;
48
+ /** The feature family it needs, or null for an uncompressed format. */
49
+ family: CompressionFamily | null;
50
+ }
51
+ /**
52
+ * The block footprint of `format`: 4×4 blocks of 8 bytes (BC1, BC4, ETC2 RGB8 / RGB8A1, EAC
53
+ * R11) or 16 (BC2/3/5/6H/7, ETC2 RGBA8, EAC RG11), ASTC's W×H blocks of 16 bytes, and 1×1
54
+ * "blocks" of a texel for the uncompressed formats. Null for a format it does not know.
55
+ */
56
+ export declare function blockInfo(format: GPUTextureFormat): BlockInfo | null;
57
+ /** Options of `uploadKtx2`. */
58
+ export interface UploadKtx2Options {
59
+ /**
60
+ * Upload as the sRGB (true) or linear (false) twin of the file's format, when it has one: a
61
+ * colour map stored as `bc7-rgba-unorm` reads as `bc7-rgba-unorm-srgb` with `srgb: true`.
62
+ * Default: the file's own format.
63
+ */
64
+ srgb?: boolean;
65
+ /** Texture label. Default `'ktx2'`. */
66
+ label?: string;
67
+ }
68
+ /** What `uploadKtx2` needs of a renderer: its device and caps. */
69
+ export interface CompressedUploadTarget {
70
+ readonly device: GPUDevice;
71
+ readonly caps: RendererCaps;
72
+ }
73
+ /** One level's copy: its block-aligned pitch, rows and copy extent (`ktx2LevelLayout`). */
74
+ export interface Ktx2LevelLayout {
75
+ bytesPerRow: number;
76
+ rowsPerImage: number;
77
+ /** The copy extent in texels: the level's size rounded up to whole blocks. */
78
+ width: number;
79
+ height: number;
80
+ /** Bytes one image (layer or face) of the level holds. */
81
+ imageBytes: number;
82
+ }
83
+ /**
84
+ * The copy layout of mip level `level` of a `width × height` texture in blocks of `block`:
85
+ * `bytesPerRow = ceil(w / bw) · blockBytes`, `rowsPerImage = ceil(h / bh)`, the extent rounded up
86
+ * to whole blocks (a 2×2 mip of a BC texture is copied as one 4×4 block).
87
+ */
88
+ export declare function ktx2LevelLayout(width: number, height: number, level: number, block: BlockInfo): Ktx2LevelLayout;
89
+ /**
90
+ * Upload a parsed KTX2 file as a `Texture` in its own format (`ktx2ToWebGpuFormat`, through
91
+ * `opts.srgb`'s colour space): a 2D texture, or a 2D array for a file with layers or cube faces
92
+ * (layer-major, faces within). Every level the file carries is written with
93
+ * `queue.writeTexture` at its block-aligned `bytesPerRow` (`ktx2LevelLayout`); the texture has
94
+ * exactly that many mips. The result binds anywhere a `TextureLike` does (a standard
95
+ * material's map).
96
+ *
97
+ * Throws `CompressedTextureError` when the payload needs a transcoder (BasisLZ, UASTC,
98
+ * `vkFormat` 0, or Zstd / ZLIB supercompression: none is bundled), when WebGPU has no such
99
+ * format, or when the device lacks the format's family (`renderer.caps.compressed`; the
100
+ * message names the format and the caps). Throws `Error` for a 3D texture, a block-compressed
101
+ * level 0 that is not whole blocks, or a level shorter than its layout.
102
+ */
103
+ export declare function uploadKtx2(renderer: CompressedUploadTarget, ktx2: Ktx2Texture, opts?: UploadKtx2Options): Texture;
104
+ export {};
@@ -0,0 +1,248 @@
1
+ /**
2
+ * Block-compressed textures (ws/render-5): which compression families the device has
3
+ * (`RendererCaps`), the features to ask an adapter for, and `uploadKtx2`, which puts a parsed
4
+ * KTX2 file (`@thunderclap-engine/engine/assets` `parseKtx2`) on the GPU in its own format, every mip
5
+ * level written with its block-aligned row pitch. No transcoder: a Basis Universal (BasisLZ /
6
+ * UASTC) or supercompressed payload, or a format the device lacks, throws
7
+ * `CompressedTextureError`.
8
+ */
9
+ import { ktx2ToWebGpuFormat } from '../assets/ktx2.js';
10
+ import { Texture } from './texture.js';
11
+ /** The WebGPU feature of each family. */
12
+ export const COMPRESSION_FEATURES = {
13
+ bc: 'texture-compression-bc',
14
+ etc2: 'texture-compression-etc2',
15
+ astc: 'texture-compression-astc',
16
+ };
17
+ /**
18
+ * The compression features `adapter` offers, to spread into `requestDevice`'s
19
+ * `requiredFeatures` (a device only gets the features it asks for):
20
+ * `adapter.requestDevice({ requiredFeatures: [...compressionFeatures(adapter)] })`.
21
+ */
22
+ export function compressionFeatures(adapter) {
23
+ const out = [];
24
+ const f = adapter.features;
25
+ if (!f || typeof f.has !== 'function')
26
+ return out;
27
+ for (const k of ['bc', 'etc2', 'astc']) {
28
+ if (f.has(COMPRESSION_FEATURES[k]))
29
+ out.push(COMPRESSION_FEATURES[k]);
30
+ }
31
+ return out;
32
+ }
33
+ /** The caps of `device`, read from its features (none on a device without a feature set). */
34
+ export function rendererCaps(device) {
35
+ const f = device.features;
36
+ const has = (k) => !!f && typeof f.has === 'function' && f.has(COMPRESSION_FEATURES[k]);
37
+ return { compressed: { bc: has('bc'), etc2: has('etc2'), astc: has('astc') } };
38
+ }
39
+ /** Why `uploadKtx2` could not upload a file: names the format and the device's caps. */
40
+ export class CompressedTextureError extends Error {
41
+ /** The file's WebGPU format, or `vk:<VkFormat>` when WebGPU has none. */
42
+ format;
43
+ /** The family the format needs, or null (no WebGPU twin, or a payload to transcode). */
44
+ family;
45
+ caps;
46
+ constructor(message, format, family, caps) {
47
+ super(message);
48
+ this.name = 'CompressedTextureError';
49
+ this.format = format;
50
+ this.family = family;
51
+ this.caps = caps;
52
+ }
53
+ }
54
+ const PLAIN_BYTES = {
55
+ r8unorm: 1,
56
+ r8snorm: 1,
57
+ r8uint: 1,
58
+ r8sint: 1,
59
+ rg8unorm: 2,
60
+ rg8snorm: 2,
61
+ rg8uint: 2,
62
+ rg8sint: 2,
63
+ r16uint: 2,
64
+ r16sint: 2,
65
+ r16float: 2,
66
+ depth16unorm: 2,
67
+ rgba8unorm: 4,
68
+ 'rgba8unorm-srgb': 4,
69
+ rgba8snorm: 4,
70
+ rgba8uint: 4,
71
+ rgba8sint: 4,
72
+ bgra8unorm: 4,
73
+ 'bgra8unorm-srgb': 4,
74
+ rgb10a2unorm: 4,
75
+ rg11b10ufloat: 4,
76
+ rgb9e5ufloat: 4,
77
+ rg16uint: 4,
78
+ rg16sint: 4,
79
+ rg16float: 4,
80
+ r32uint: 4,
81
+ r32sint: 4,
82
+ r32float: 4,
83
+ depth32float: 4,
84
+ rgba16uint: 8,
85
+ rgba16sint: 8,
86
+ rgba16float: 8,
87
+ rg32uint: 8,
88
+ rg32sint: 8,
89
+ rg32float: 8,
90
+ rgba32uint: 16,
91
+ rgba32sint: 16,
92
+ rgba32float: 16,
93
+ };
94
+ /**
95
+ * The block footprint of `format`: 4×4 blocks of 8 bytes (BC1, BC4, ETC2 RGB8 / RGB8A1, EAC
96
+ * R11) or 16 (BC2/3/5/6H/7, ETC2 RGBA8, EAC RG11), ASTC's W×H blocks of 16 bytes, and 1×1
97
+ * "blocks" of a texel for the uncompressed formats. Null for a format it does not know.
98
+ */
99
+ export function blockInfo(format) {
100
+ const f = format;
101
+ if (f.startsWith('bc')) {
102
+ const eight = f.startsWith('bc1-') || f.startsWith('bc4-');
103
+ return { width: 4, height: 4, bytes: eight ? 8 : 16, family: 'bc' };
104
+ }
105
+ if (f.startsWith('etc2-')) {
106
+ return { width: 4, height: 4, bytes: f.startsWith('etc2-rgba8') ? 16 : 8, family: 'etc2' };
107
+ }
108
+ if (f.startsWith('eac-')) {
109
+ return { width: 4, height: 4, bytes: f.startsWith('eac-rg11') ? 16 : 8, family: 'etc2' };
110
+ }
111
+ if (f.startsWith('astc-')) {
112
+ const m = /^astc-(\d+)x(\d+)-/.exec(f);
113
+ if (!m)
114
+ return null;
115
+ return { width: Number(m[1]), height: Number(m[2]), bytes: 16, family: 'astc' };
116
+ }
117
+ const n = PLAIN_BYTES[f];
118
+ return n ? { width: 1, height: 1, bytes: n, family: null } : null;
119
+ }
120
+ /** `format` in sRGB (`srgb` true) or linear (false) form, when it has that twin. */
121
+ function withColorSpace(format, srgb) {
122
+ if (srgb === undefined)
123
+ return format;
124
+ const f = format;
125
+ if (srgb) {
126
+ if (f.endsWith('-srgb'))
127
+ return format;
128
+ const twin = `${f}-srgb`;
129
+ return SRGB_TWINS.has(twin) ? twin : format;
130
+ }
131
+ return (f.endsWith('-srgb') ? f.slice(0, -5) : f);
132
+ }
133
+ /** The formats that have an `-srgb` form. */
134
+ const SRGB_TWINS = new Set([
135
+ 'rgba8unorm-srgb',
136
+ 'bgra8unorm-srgb',
137
+ 'bc1-rgba-unorm-srgb',
138
+ 'bc2-rgba-unorm-srgb',
139
+ 'bc3-rgba-unorm-srgb',
140
+ 'bc7-rgba-unorm-srgb',
141
+ 'etc2-rgb8unorm-srgb',
142
+ 'etc2-rgb8a1unorm-srgb',
143
+ 'etc2-rgba8unorm-srgb',
144
+ ...[
145
+ '4x4',
146
+ '5x4',
147
+ '5x5',
148
+ '6x5',
149
+ '6x6',
150
+ '8x5',
151
+ '8x6',
152
+ '8x8',
153
+ '10x5',
154
+ '10x6',
155
+ '10x8',
156
+ '10x10',
157
+ '12x10',
158
+ '12x12',
159
+ ].map((s) => `astc-${s}-unorm-srgb`),
160
+ ]);
161
+ /**
162
+ * The copy layout of mip level `level` of a `width × height` texture in blocks of `block`:
163
+ * `bytesPerRow = ceil(w / bw) · blockBytes`, `rowsPerImage = ceil(h / bh)`, the extent rounded up
164
+ * to whole blocks (a 2×2 mip of a BC texture is copied as one 4×4 block).
165
+ */
166
+ export function ktx2LevelLayout(width, height, level, block) {
167
+ const w = Math.max(1, width >> level);
168
+ const h = Math.max(1, height >> level);
169
+ const bx = Math.ceil(w / block.width);
170
+ const by = Math.ceil(h / block.height);
171
+ const bytesPerRow = bx * block.bytes;
172
+ return {
173
+ bytesPerRow,
174
+ rowsPerImage: by,
175
+ width: bx * block.width,
176
+ height: by * block.height,
177
+ imageBytes: bytesPerRow * by,
178
+ };
179
+ }
180
+ /**
181
+ * Upload a parsed KTX2 file as a `Texture` in its own format (`ktx2ToWebGpuFormat`, through
182
+ * `opts.srgb`'s colour space): a 2D texture, or a 2D array for a file with layers or cube faces
183
+ * (layer-major, faces within). Every level the file carries is written with
184
+ * `queue.writeTexture` at its block-aligned `bytesPerRow` (`ktx2LevelLayout`); the texture has
185
+ * exactly that many mips. The result binds anywhere a `TextureLike` does (a standard
186
+ * material's map).
187
+ *
188
+ * Throws `CompressedTextureError` when the payload needs a transcoder (BasisLZ, UASTC,
189
+ * `vkFormat` 0, or Zstd / ZLIB supercompression: none is bundled), when WebGPU has no such
190
+ * format, or when the device lacks the format's family (`renderer.caps.compressed`; the
191
+ * message names the format and the caps). Throws `Error` for a 3D texture, a block-compressed
192
+ * level 0 that is not whole blocks, or a level shorter than its layout.
193
+ */
194
+ export function uploadKtx2(renderer, ktx2, opts = {}) {
195
+ const caps = renderer.caps;
196
+ const capsText = () => {
197
+ const c = caps.compressed;
198
+ const on = ['bc', 'etc2', 'astc'].filter((k) => c[k]);
199
+ return on.length ? on.join(', ') : 'none';
200
+ };
201
+ if (ktx2.supercompression !== 'none' || ktx2.vkFormat === 0 || ktx2.levels.length === 0) {
202
+ const what = ktx2.supercompression !== 'none' ? ktx2.supercompression : 'basis';
203
+ throw new CompressedTextureError(`uploadKtx2: the ${what} payload needs a transcoder (none is bundled); compressed caps: ${capsText()}`, `vk:${ktx2.vkFormat}`, null, caps);
204
+ }
205
+ const fileFormat = ktx2ToWebGpuFormat(ktx2.vkFormat);
206
+ if (!fileFormat) {
207
+ throw new CompressedTextureError(`uploadKtx2: VkFormat ${ktx2.vkFormat} has no WebGPU format; compressed caps: ${capsText()}`, `vk:${ktx2.vkFormat}`, null, caps);
208
+ }
209
+ const format = withColorSpace(fileFormat, opts.srgb);
210
+ const block = blockInfo(format);
211
+ if (!block) {
212
+ throw new CompressedTextureError(`uploadKtx2: format ${format} is not uploadable; compressed caps: ${capsText()}`, format, null, caps);
213
+ }
214
+ if (block.family && !caps.compressed[block.family]) {
215
+ throw new CompressedTextureError(`uploadKtx2: format ${format} needs ${COMPRESSION_FEATURES[block.family]}, which the device lacks; compressed caps: ${capsText()}`, format, block.family, caps);
216
+ }
217
+ if (ktx2.depth > 1)
218
+ throw new Error('uploadKtx2: 3D textures are not supported');
219
+ const width = Math.max(1, ktx2.width);
220
+ const height = Math.max(1, ktx2.height);
221
+ if (width % block.width !== 0 || height % block.height !== 0) {
222
+ throw new Error(`uploadKtx2: ${width}×${height} is not whole ${block.width}×${block.height} blocks of ${format}`);
223
+ }
224
+ const images = Math.max(1, ktx2.layers) * Math.max(1, ktx2.faces);
225
+ const array = ktx2.layers > 0 || ktx2.faces > 1;
226
+ const levels = ktx2.levels;
227
+ const tex = new Texture(renderer.device, {
228
+ width,
229
+ height,
230
+ ...(array ? { layers: images } : {}),
231
+ mipLevelCount: levels.length,
232
+ format,
233
+ usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST,
234
+ label: opts.label ?? 'ktx2',
235
+ });
236
+ const queue = renderer.device.queue;
237
+ for (let i = 0; i < levels.length; i++) {
238
+ const lv = ktx2LevelLayout(width, height, i, block);
239
+ const bytes = levels[i].bytes;
240
+ const need = lv.imageBytes * images;
241
+ if (bytes.byteLength < need) {
242
+ tex.destroy();
243
+ throw new Error(`uploadKtx2: level ${i} holds ${bytes.byteLength} bytes, needs ${need}`);
244
+ }
245
+ queue.writeTexture({ texture: tex.texture, mipLevel: i }, bytes, { offset: 0, bytesPerRow: lv.bytesPerRow, rowsPerImage: lv.rowsPerImage }, [lv.width, lv.height, images]);
246
+ }
247
+ return tex;
248
+ }
@@ -0,0 +1,15 @@
1
+ /**
2
+ * GPU error reporting. Every WGSL compile error and uncaptured validation error goes through
3
+ * `reportGpuError`, which logs loudly and keeps the last messages for tests/debug overlays.
4
+ */
5
+ export declare const gpuDiagnostics: {
6
+ /** Most recent error messages (bounded). */
7
+ errors: string[];
8
+ /** Total errors reported since load. */
9
+ errorCount: number;
10
+ };
11
+ export declare function reportGpuError(message: string): void;
12
+ /** Subscribe to GPU errors (e.g. to show a dev toast). Returns unsubscribe. */
13
+ export declare function onGpuError(fn: (message: string) => void): () => void;
14
+ /** Route uncaptured device errors (validation, OOM, internal) to `reportGpuError`. */
15
+ export declare function installGpuErrorLogging(device: GPUDevice): void;
@@ -0,0 +1,43 @@
1
+ /**
2
+ * GPU error reporting. Every WGSL compile error and uncaptured validation error goes through
3
+ * `reportGpuError`, which logs loudly and keeps the last messages for tests/debug overlays.
4
+ */
5
+ const MAX_KEPT = 50;
6
+ export const gpuDiagnostics = {
7
+ /** Most recent error messages (bounded). */
8
+ errors: [],
9
+ /** Total errors reported since load. */
10
+ errorCount: 0,
11
+ };
12
+ const listeners = new Set();
13
+ export function reportGpuError(message) {
14
+ console.error(message);
15
+ gpuDiagnostics.errorCount++;
16
+ gpuDiagnostics.errors.push(message);
17
+ if (gpuDiagnostics.errors.length > MAX_KEPT)
18
+ gpuDiagnostics.errors.shift();
19
+ for (const fn of listeners)
20
+ fn(message);
21
+ }
22
+ /** Subscribe to GPU errors (e.g. to show a dev toast). Returns unsubscribe. */
23
+ export function onGpuError(fn) {
24
+ listeners.add(fn);
25
+ return () => listeners.delete(fn);
26
+ }
27
+ const logged = new WeakSet();
28
+ /** Route uncaptured device errors (validation, OOM, internal) to `reportGpuError`. */
29
+ export function installGpuErrorLogging(device) {
30
+ // Several renderers may share a device (e.g. a SnapshotRenderer); log each error once.
31
+ if (logged.has(device))
32
+ return;
33
+ logged.add(device);
34
+ device.addEventListener('uncapturederror', (ev) => {
35
+ const e = ev.error;
36
+ const kind = e instanceof GPUValidationError
37
+ ? 'validation'
38
+ : e instanceof GPUOutOfMemoryError
39
+ ? 'out-of-memory'
40
+ : 'internal';
41
+ reportGpuError(`[gpu] uncaptured ${kind} error: ${e.message}`);
42
+ });
43
+ }
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Dynamic resolution: a small controller that turns GPU frame-time samples into a render
3
+ * scale. Pure (no GPU, no clock): feed it `sample(gpuMs, nowMs)` and apply the returned scale
4
+ * with `Renderer.setResolution({ scale })` when it changes.
5
+ *
6
+ * Design goals: steady image, no pumping.
7
+ * - Drops only after a sustained over-budget window (`dropAfterMs`), one step at a time.
8
+ * - Raises only after a longer window of clear headroom (`raiseAfterMs`), and only when the
9
+ * predicted cost at the higher scale (time ∝ pixels ∝ scale²) stays under
10
+ * `budgetMs × raiseMargin`. That gap between drop and raise thresholds is the hysteresis.
11
+ * - A cooldown after every change lets the new size settle before the next decision.
12
+ * - A raise that is followed quickly by a drop "burns" that level: the controller will not try
13
+ * to raise past it again for `burnMs`, so a scene that sits right at the edge settles
14
+ * instead of oscillating.
15
+ */
16
+ export interface DynamicResolutionOptions {
17
+ /** GPU time budget per frame (ms). Default 14 (headroom under a 16.7 ms vsync). */
18
+ budgetMs?: number;
19
+ /** Lowest scale. Default 0.5. */
20
+ minScale?: number;
21
+ /** Highest scale. Default 1. */
22
+ maxScale?: number;
23
+ /** Step per change. Default 0.1. */
24
+ step?: number;
25
+ /** Sustained over-budget time before dropping (ms). Default 1000. */
26
+ dropAfterMs?: number;
27
+ /** Sustained headroom before raising (ms). Default 3000. */
28
+ raiseAfterMs?: number;
29
+ /** Predicted time at the higher scale must be below budget × this. Default 0.85. */
30
+ raiseMargin?: number;
31
+ /** Quiet time after any change (ms). Default 1500. */
32
+ cooldownMs?: number;
33
+ /** How long a level that dropped right after a raise stays off limits (ms). Default 20000. */
34
+ burnMs?: number;
35
+ /** Smoothing for the time estimate, 0..1 (weight of each new sample). Default 0.2. */
36
+ smoothing?: number;
37
+ }
38
+ export declare class DynamicResolution {
39
+ readonly budgetMs: number;
40
+ readonly minScale: number;
41
+ readonly maxScale: number;
42
+ private readonly step;
43
+ private readonly dropAfterMs;
44
+ private readonly raiseAfterMs;
45
+ private readonly raiseMargin;
46
+ private readonly cooldownMs;
47
+ private readonly burnMs;
48
+ private readonly smoothing;
49
+ private _scale;
50
+ private ema;
51
+ private overSince;
52
+ private underSince;
53
+ private lastChange;
54
+ private lastRaise;
55
+ /** Scale above which we will not raise until `ceilingUntil`. */
56
+ private ceiling;
57
+ private ceilingUntil;
58
+ constructor(opts?: DynamicResolutionOptions);
59
+ /** Current scale. */
60
+ get scale(): number;
61
+ /** Smoothed GPU time (ms), or -1 before the first sample. */
62
+ get estimateMs(): number;
63
+ /** True when the scale is at its floor and frames are still over budget. */
64
+ get saturated(): boolean;
65
+ /** Forget history and return to `scale` (default max), e.g. on a scene change. */
66
+ reset(scale?: number, nowMs?: number): void;
67
+ /** Feed a GPU time sample. Returns the (possibly new) scale. */
68
+ sample(gpuMs: number, nowMs: number): number;
69
+ private change;
70
+ }
@@ -0,0 +1,126 @@
1
+ /**
2
+ * Dynamic resolution: a small controller that turns GPU frame-time samples into a render
3
+ * scale. Pure (no GPU, no clock): feed it `sample(gpuMs, nowMs)` and apply the returned scale
4
+ * with `Renderer.setResolution({ scale })` when it changes.
5
+ *
6
+ * Design goals: steady image, no pumping.
7
+ * - Drops only after a sustained over-budget window (`dropAfterMs`), one step at a time.
8
+ * - Raises only after a longer window of clear headroom (`raiseAfterMs`), and only when the
9
+ * predicted cost at the higher scale (time ∝ pixels ∝ scale²) stays under
10
+ * `budgetMs × raiseMargin`. That gap between drop and raise thresholds is the hysteresis.
11
+ * - A cooldown after every change lets the new size settle before the next decision.
12
+ * - A raise that is followed quickly by a drop "burns" that level: the controller will not try
13
+ * to raise past it again for `burnMs`, so a scene that sits right at the edge settles
14
+ * instead of oscillating.
15
+ */
16
+ export class DynamicResolution {
17
+ budgetMs;
18
+ minScale;
19
+ maxScale;
20
+ step;
21
+ dropAfterMs;
22
+ raiseAfterMs;
23
+ raiseMargin;
24
+ cooldownMs;
25
+ burnMs;
26
+ smoothing;
27
+ _scale;
28
+ ema = -1;
29
+ overSince = -1;
30
+ underSince = -1;
31
+ lastChange = Number.NEGATIVE_INFINITY;
32
+ lastRaise = Number.NEGATIVE_INFINITY;
33
+ /** Scale above which we will not raise until `ceilingUntil`. */
34
+ ceiling = Number.POSITIVE_INFINITY;
35
+ ceilingUntil = Number.NEGATIVE_INFINITY;
36
+ constructor(opts = {}) {
37
+ this.budgetMs = opts.budgetMs ?? 14;
38
+ this.minScale = opts.minScale ?? 0.5;
39
+ this.maxScale = opts.maxScale ?? 1;
40
+ this.step = opts.step ?? 0.1;
41
+ this.dropAfterMs = opts.dropAfterMs ?? 1000;
42
+ this.raiseAfterMs = opts.raiseAfterMs ?? 3000;
43
+ this.raiseMargin = opts.raiseMargin ?? 0.85;
44
+ this.cooldownMs = opts.cooldownMs ?? 1500;
45
+ this.burnMs = opts.burnMs ?? 20000;
46
+ this.smoothing = opts.smoothing ?? 0.2;
47
+ this._scale = this.maxScale;
48
+ }
49
+ /** Current scale. */
50
+ get scale() {
51
+ return this._scale;
52
+ }
53
+ /** Smoothed GPU time (ms), or -1 before the first sample. */
54
+ get estimateMs() {
55
+ return this.ema;
56
+ }
57
+ /** True when the scale is at its floor and frames are still over budget. */
58
+ get saturated() {
59
+ return this._scale <= this.minScale + 1e-6 && this.overSince >= 0;
60
+ }
61
+ /** Forget history and return to `scale` (default max), e.g. on a scene change. */
62
+ reset(scale = this.maxScale, nowMs = Number.NEGATIVE_INFINITY) {
63
+ this._scale = clamp(scale, this.minScale, this.maxScale);
64
+ this.ema = -1;
65
+ this.overSince = -1;
66
+ this.underSince = -1;
67
+ this.lastChange = nowMs;
68
+ }
69
+ /** Feed a GPU time sample. Returns the (possibly new) scale. */
70
+ sample(gpuMs, nowMs) {
71
+ if (!(gpuMs >= 0) || !Number.isFinite(gpuMs))
72
+ return this._scale;
73
+ // Samples right after a change describe the old size; let them flush.
74
+ if (nowMs - this.lastChange < this.cooldownMs) {
75
+ this.ema = gpuMs;
76
+ this.overSince = -1;
77
+ this.underSince = -1;
78
+ return this._scale;
79
+ }
80
+ this.ema = this.ema < 0 ? gpuMs : this.ema + (gpuMs - this.ema) * this.smoothing;
81
+ if (this.ema > this.budgetMs) {
82
+ this.underSince = -1;
83
+ if (this.overSince < 0)
84
+ this.overSince = nowMs;
85
+ if (nowMs - this.overSince >= this.dropAfterMs && this._scale > this.minScale + 1e-6) {
86
+ // A drop soon after a raise means that level is too expensive: remember it.
87
+ if (nowMs - this.lastRaise < this.raiseAfterMs + this.cooldownMs) {
88
+ this.ceiling = this._scale - 1e-6;
89
+ this.ceilingUntil = nowMs + this.burnMs;
90
+ }
91
+ // Drop by enough to get under budget, at least one step, at most three.
92
+ const want = this._scale * Math.sqrt((this.budgetMs * this.raiseMargin) / this.ema);
93
+ const steps = clamp(Math.ceil((this._scale - want) / this.step), 1, 3);
94
+ this.change(this._scale - steps * this.step, nowMs);
95
+ this.overSince = nowMs;
96
+ }
97
+ return this._scale;
98
+ }
99
+ this.overSince = -1;
100
+ const next = Math.min(this.maxScale, this._scale + this.step);
101
+ const predicted = this.ema * (next / this._scale) ** 2;
102
+ const blocked = nowMs < this.ceilingUntil && next > this.ceiling;
103
+ if (next > this._scale + 1e-6 && !blocked && predicted < this.budgetMs * this.raiseMargin) {
104
+ if (this.underSince < 0)
105
+ this.underSince = nowMs;
106
+ if (nowMs - this.underSince >= this.raiseAfterMs) {
107
+ this.change(next, nowMs);
108
+ this.lastRaise = nowMs;
109
+ }
110
+ }
111
+ else {
112
+ this.underSince = -1;
113
+ }
114
+ return this._scale;
115
+ }
116
+ change(scale, nowMs) {
117
+ // Snap to the step grid so repeated changes don't accumulate float drift.
118
+ const snapped = Math.round(scale / this.step) * this.step;
119
+ this._scale = clamp(Number(snapped.toFixed(4)), this.minScale, this.maxScale);
120
+ this.lastChange = nowMs;
121
+ this.underSince = -1;
122
+ }
123
+ }
124
+ function clamp(x, lo, hi) {
125
+ return Math.min(hi, Math.max(lo, x));
126
+ }