@driftengine/core 3.63.0 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (428) hide show
  1. package/CHANGELOG.json +111 -0
  2. package/NOTICE +20 -0
  3. package/README.md +1 -1
  4. package/dist/core/loop.js +18 -2
  5. package/dist/core/storage.js +5 -0
  6. package/dist/geometry/meshBuilder.d.ts +1 -1
  7. package/dist/geometry/meshBuilder.js +6 -2
  8. package/dist/index.d.ts +69 -4
  9. package/dist/index.js +112 -2
  10. package/dist/input/input.js +6 -1
  11. package/dist/math/exact.d.ts +13 -3
  12. package/dist/math/exact.js +68 -3
  13. package/dist/math/frustum.d.ts +17 -2
  14. package/dist/math/frustum.js +24 -0
  15. package/dist/render/backend/api.d.ts +34 -1
  16. package/dist/render/backend/api.js +22 -1
  17. package/dist/render/backend/budget.d.ts +7 -6
  18. package/dist/render/backend/budget.js +7 -6
  19. package/dist/render/backend/createRenderer.d.ts +57 -0
  20. package/dist/render/backend/createRenderer.js +51 -2
  21. package/dist/render/backend/materialChanges.d.ts +41 -0
  22. package/dist/render/backend/materialChanges.js +38 -0
  23. package/dist/render/backend/select.js +8 -0
  24. package/dist/render/backend/webgl2/renderer.d.ts +80 -1
  25. package/dist/render/backend/webgl2/renderer.js +208 -10
  26. package/dist/render/backend/webgpu/buffers.d.ts +20 -1
  27. package/dist/render/backend/webgpu/buffers.js +116 -58
  28. package/dist/render/backend/webgpu/fieldCompose.d.ts +156 -0
  29. package/dist/render/backend/webgpu/fieldCompose.js +396 -0
  30. package/dist/render/backend/webgpu/giFieldPass.d.ts +66 -0
  31. package/dist/render/backend/webgpu/giFieldPass.js +137 -0
  32. package/dist/render/backend/webgpu/gpuDrivenPass.d.ts +728 -0
  33. package/dist/render/backend/webgpu/gpuDrivenPass.js +2473 -0
  34. package/dist/render/backend/webgpu/linePass.d.ts +4 -14
  35. package/dist/render/backend/webgpu/linePass.js +4 -33
  36. package/dist/render/backend/webgpu/probeBake.d.ts +148 -0
  37. package/dist/render/backend/webgpu/probeBake.js +493 -0
  38. package/dist/render/backend/webgpu/renderer.d.ts +379 -24
  39. package/dist/render/backend/webgpu/renderer.js +1369 -76
  40. package/dist/render/backend/webgpu/scatterPass.js +15 -1
  41. package/dist/render/backend/webgpu/surfaceTexturePass.d.ts +19 -9
  42. package/dist/render/backend/webgpu/surfaceTexturePass.js +58 -32
  43. package/dist/render/causticsRenderer.d.ts +3 -1
  44. package/dist/render/causticsRenderer.js +4 -1
  45. package/dist/render/depthConvention.d.ts +12 -0
  46. package/dist/render/depthConvention.js +14 -0
  47. package/dist/render/deviceFeatures.d.ts +32 -0
  48. package/dist/render/deviceFeatures.js +41 -0
  49. package/dist/render/fog.d.ts +79 -0
  50. package/dist/render/fog.js +138 -0
  51. package/dist/render/frame/alias.d.ts +30 -0
  52. package/dist/render/frame/alias.js +61 -0
  53. package/dist/render/frame/deps.d.ts +37 -0
  54. package/dist/render/frame/deps.js +122 -0
  55. package/dist/render/frame/flushGraph.d.ts +43 -0
  56. package/dist/render/frame/flushGraph.js +84 -0
  57. package/dist/render/frame/graphSchedule.d.ts +49 -0
  58. package/dist/render/frame/graphSchedule.js +120 -0
  59. package/dist/render/frame/index.d.ts +15 -0
  60. package/dist/render/frame/index.js +8 -0
  61. package/dist/render/frame/lifetime.d.ts +24 -0
  62. package/dist/render/frame/lifetime.js +36 -0
  63. package/dist/render/frame/passTimings.d.ts +42 -0
  64. package/dist/render/frame/passTimings.js +38 -0
  65. package/dist/render/frame/validate.d.ts +15 -0
  66. package/dist/render/frame/validate.js +22 -0
  67. package/dist/render/frame/virtual.d.ts +44 -0
  68. package/dist/render/frame/virtual.js +61 -0
  69. package/dist/render/gi/chain.d.ts +82 -0
  70. package/dist/render/gi/chain.js +135 -0
  71. package/dist/render/gi/denoise.d.ts +114 -0
  72. package/dist/render/gi/denoise.js +169 -0
  73. package/dist/render/gi/fieldScene.d.ts +125 -0
  74. package/dist/render/gi/fieldScene.js +219 -0
  75. package/dist/render/gi/globalField.d.ts +157 -0
  76. package/dist/render/gi/globalField.js +373 -0
  77. package/dist/render/gi/probeGridFit.d.ts +24 -0
  78. package/dist/render/gi/probeGridFit.js +71 -0
  79. package/dist/render/gi/probeTrace.d.ts +82 -0
  80. package/dist/render/gi/probeTrace.js +128 -0
  81. package/dist/render/gi/probeVolume.d.ts +136 -0
  82. package/dist/render/gi/probeVolume.js +191 -0
  83. package/dist/render/gi/reflection.d.ts +73 -0
  84. package/dist/render/gi/reflection.js +109 -0
  85. package/dist/render/gi/traceField.d.ts +91 -0
  86. package/dist/render/gi/traceField.js +129 -0
  87. package/dist/render/gi/traceScreen.d.ts +68 -0
  88. package/dist/render/gi/traceScreen.js +82 -0
  89. package/dist/render/gpuScene.d.ts +38 -0
  90. package/dist/render/gpuScene.js +41 -0
  91. package/dist/render/gpudriven/alphaTest.d.ts +28 -0
  92. package/dist/render/gpudriven/alphaTest.js +32 -0
  93. package/dist/render/gpudriven/clusterUpload.d.ts +59 -0
  94. package/dist/render/gpudriven/clusterUpload.js +75 -0
  95. package/dist/render/gpudriven/clusterWorld.d.ts +57 -0
  96. package/dist/render/gpudriven/clusterWorld.js +155 -0
  97. package/dist/render/gpudriven/compact.d.ts +51 -0
  98. package/dist/render/gpudriven/compact.js +68 -0
  99. package/dist/render/gpudriven/cullClusters.d.ts +129 -0
  100. package/dist/render/gpudriven/cullClusters.js +237 -0
  101. package/dist/render/gpudriven/decodeTables.d.ts +64 -0
  102. package/dist/render/gpudriven/decodeTables.js +219 -0
  103. package/dist/render/gpudriven/frustum.d.ts +25 -0
  104. package/dist/render/gpudriven/frustum.js +59 -0
  105. package/dist/render/gpudriven/geometryHarness.d.ts +15 -0
  106. package/dist/render/gpudriven/geometryHarness.js +31 -0
  107. package/dist/render/gpudriven/hzb.d.ts +60 -0
  108. package/dist/render/gpudriven/hzb.js +97 -0
  109. package/dist/render/gpudriven/ibl.d.ts +85 -0
  110. package/dist/render/gpudriven/ibl.js +129 -0
  111. package/dist/render/gpudriven/indirect.d.ts +67 -0
  112. package/dist/render/gpudriven/indirect.js +77 -0
  113. package/dist/render/gpudriven/instances.d.ts +43 -0
  114. package/dist/render/gpudriven/instances.js +172 -0
  115. package/dist/render/gpudriven/lit.d.ts +165 -0
  116. package/dist/render/gpudriven/lit.js +165 -0
  117. package/dist/render/gpudriven/lodCut.d.ts +28 -0
  118. package/dist/render/gpudriven/lodCut.js +38 -0
  119. package/dist/render/gpudriven/materialBin.d.ts +60 -0
  120. package/dist/render/gpudriven/materialBin.js +108 -0
  121. package/dist/render/gpudriven/materialTable.d.ts +88 -0
  122. package/dist/render/gpudriven/materialTable.js +115 -0
  123. package/dist/render/gpudriven/pipeline.d.ts +135 -0
  124. package/dist/render/gpudriven/pipeline.js +237 -0
  125. package/dist/render/gpudriven/rangeAlloc.d.ts +50 -0
  126. package/dist/render/gpudriven/rangeAlloc.js +112 -0
  127. package/dist/render/gpudriven/sceneUpload.d.ts +96 -0
  128. package/dist/render/gpudriven/sceneUpload.js +115 -0
  129. package/dist/render/gpudriven/shadeBins.d.ts +92 -0
  130. package/dist/render/gpudriven/shadeBins.js +182 -0
  131. package/dist/render/gpudriven/shadow.d.ts +136 -0
  132. package/dist/render/gpudriven/shadow.js +224 -0
  133. package/dist/render/gpudriven/shadowCamera.d.ts +87 -0
  134. package/dist/render/gpudriven/shadowCamera.js +345 -0
  135. package/dist/render/gpudriven/streamScene.d.ts +194 -0
  136. package/dist/render/gpudriven/streamScene.js +459 -0
  137. package/dist/render/gpudriven/surfaceFrame.d.ts +25 -0
  138. package/dist/render/gpudriven/surfaceFrame.js +51 -0
  139. package/dist/render/gpudriven/twoPhase.d.ts +122 -0
  140. package/dist/render/gpudriven/twoPhase.js +160 -0
  141. package/dist/render/gpudriven/visbuffer.d.ts +25 -0
  142. package/dist/render/gpudriven/visbuffer.js +37 -0
  143. package/dist/render/halfFloat.d.ts +10 -0
  144. package/dist/render/halfFloat.js +23 -4
  145. package/dist/render/inference/attentionTiled.d.ts +27 -0
  146. package/dist/render/inference/attentionTiled.js +117 -0
  147. package/dist/render/inference/denseKernels.d.ts +29 -0
  148. package/dist/render/inference/denseKernels.js +312 -0
  149. package/dist/render/inference/device.d.ts +21 -0
  150. package/dist/render/inference/device.js +32 -0
  151. package/dist/render/inference/deviceGraph.d.ts +34 -0
  152. package/dist/render/inference/deviceGraph.js +17 -0
  153. package/dist/render/inference/fuse.d.ts +15 -0
  154. package/dist/render/inference/fuse.js +37 -0
  155. package/dist/render/inference/kernelKit.d.ts +52 -0
  156. package/dist/render/inference/kernelKit.js +70 -0
  157. package/dist/render/inference/kernels.d.ts +3 -0
  158. package/dist/render/inference/kernels.js +11 -0
  159. package/dist/render/inference/plan.d.ts +13 -0
  160. package/dist/render/inference/plan.js +18 -0
  161. package/dist/render/inference/reuse.d.ts +39 -0
  162. package/dist/render/inference/reuse.js +65 -0
  163. package/dist/render/inference/runner.d.ts +31 -0
  164. package/dist/render/inference/runner.js +175 -0
  165. package/dist/render/inference/schedule.d.ts +39 -0
  166. package/dist/render/inference/schedule.js +59 -0
  167. package/dist/render/inference/shapeKernels.d.ts +10 -0
  168. package/dist/render/inference/shapeKernels.js +80 -0
  169. package/dist/render/inference/spatialKernels.d.ts +21 -0
  170. package/dist/render/inference/spatialKernels.js +179 -0
  171. package/dist/render/inference/splatRaster.wgsl.d.ts +75 -0
  172. package/dist/render/inference/splatRaster.wgsl.js +337 -0
  173. package/dist/render/inference/splatTiles.d.ts +79 -0
  174. package/dist/render/inference/splatTiles.js +161 -0
  175. package/dist/render/lineBatch.d.ts +2 -5
  176. package/dist/render/lineBatch.js +4 -24
  177. package/dist/render/mesh.d.ts +4 -0
  178. package/dist/render/mesh.js +45 -1
  179. package/dist/render/pass.d.ts +145 -8
  180. package/dist/render/prefilterEnvMap.d.ts +22 -0
  181. package/dist/render/prefilterEnvMap.js +31 -0
  182. package/dist/render/readback.d.ts +30 -0
  183. package/dist/render/readback.js +47 -0
  184. package/dist/render/recon/clamp.d.ts +36 -0
  185. package/dist/render/recon/clamp.js +92 -0
  186. package/dist/render/recon/disocclusion.d.ts +60 -0
  187. package/dist/render/recon/disocclusion.js +109 -0
  188. package/dist/render/recon/frameGen.d.ts +81 -0
  189. package/dist/render/recon/frameGen.js +150 -0
  190. package/dist/render/recon/frameSizes.d.ts +34 -0
  191. package/dist/render/recon/frameSizes.js +58 -0
  192. package/dist/render/recon/jitter.d.ts +60 -0
  193. package/dist/render/recon/jitter.js +118 -0
  194. package/dist/render/recon/motionVectors.d.ts +55 -0
  195. package/dist/render/recon/motionVectors.js +107 -0
  196. package/dist/render/recon/reproject.d.ts +41 -0
  197. package/dist/render/recon/reproject.js +131 -0
  198. package/dist/render/recon/resolve.d.ts +90 -0
  199. package/dist/render/recon/resolve.js +313 -0
  200. package/dist/render/recon/tier.d.ts +24 -0
  201. package/dist/render/recon/tier.js +8 -0
  202. package/dist/render/reflectionProbe.d.ts +26 -0
  203. package/dist/render/reflectionProbe.js +33 -0
  204. package/dist/render/renderQuality.d.ts +69 -0
  205. package/dist/render/renderQuality.js +22 -0
  206. package/dist/render/screenSpaceReflection.d.ts +23 -0
  207. package/dist/render/screenSpaceReflection.js +22 -3
  208. package/dist/render/sdfFont.js +2 -0
  209. package/dist/render/segmentQuads.d.ts +13 -0
  210. package/dist/render/segmentQuads.js +32 -0
  211. package/dist/render/shaders/flat/lobes.d.ts +27 -0
  212. package/dist/render/shaders/flat/lobes.js +33 -3
  213. package/dist/render/shaders/flat/preamble.d.ts +7 -0
  214. package/dist/render/shaders/flat/preamble.js +24 -1
  215. package/dist/render/shaders/flat/probeGrid.d.ts +1 -1
  216. package/dist/render/shaders/flat/probeGrid.js +71 -2
  217. package/dist/render/shaders/generated/flat.wgsl.d.ts +48 -8
  218. package/dist/render/shaders/generated/flat.wgsl.js +64 -24
  219. package/dist/render/shaders/gi/composeField.wgsl.d.ts +56 -0
  220. package/dist/render/shaders/gi/composeField.wgsl.js +231 -0
  221. package/dist/render/shaders/gi/marchField.wgsl.d.ts +30 -0
  222. package/dist/render/shaders/gi/marchField.wgsl.js +141 -0
  223. package/dist/render/shaders/gi/probeBake.wgsl.d.ts +113 -0
  224. package/dist/render/shaders/gi/probeBake.wgsl.js +635 -0
  225. package/dist/render/shaders/gi/probeTrace.wgsl.d.ts +72 -0
  226. package/dist/render/shaders/gi/probeTrace.wgsl.js +261 -0
  227. package/dist/render/shaders/gi/probeVolume.wgsl.d.ts +49 -0
  228. package/dist/render/shaders/gi/probeVolume.wgsl.js +242 -0
  229. package/dist/render/shaders/gi/sampleField.wgsl.d.ts +32 -0
  230. package/dist/render/shaders/gi/sampleField.wgsl.js +134 -0
  231. package/dist/render/shaders/gpudriven/blendRaster.wgsl.d.ts +26 -0
  232. package/dist/render/shaders/gpudriven/blendRaster.wgsl.js +339 -0
  233. package/dist/render/shaders/gpudriven/compact.wgsl.d.ts +29 -0
  234. package/dist/render/shaders/gpudriven/compact.wgsl.js +58 -0
  235. package/dist/render/shaders/gpudriven/cull.wgsl.d.ts +90 -0
  236. package/dist/render/shaders/gpudriven/cull.wgsl.js +423 -0
  237. package/dist/render/shaders/gpudriven/decode.wgsl.d.ts +72 -0
  238. package/dist/render/shaders/gpudriven/decode.wgsl.js +349 -0
  239. package/dist/render/shaders/gpudriven/environment.wgsl.d.ts +44 -0
  240. package/dist/render/shaders/gpudriven/environment.wgsl.js +137 -0
  241. package/dist/render/shaders/gpudriven/fog.wgsl.d.ts +28 -0
  242. package/dist/render/shaders/gpudriven/fog.wgsl.js +107 -0
  243. package/dist/render/shaders/gpudriven/frameBlock.wgsl.d.ts +13 -0
  244. package/dist/render/shaders/gpudriven/frameBlock.wgsl.js +62 -0
  245. package/dist/render/shaders/gpudriven/materialBin.wgsl.d.ts +51 -0
  246. package/dist/render/shaders/gpudriven/materialBin.wgsl.js +112 -0
  247. package/dist/render/shaders/gpudriven/materialTable.wgsl.d.ts +25 -0
  248. package/dist/render/shaders/gpudriven/materialTable.wgsl.js +43 -0
  249. package/dist/render/shaders/gpudriven/meshTransform.wgsl.d.ts +18 -0
  250. package/dist/render/shaders/gpudriven/meshTransform.wgsl.js +37 -0
  251. package/dist/render/shaders/gpudriven/present.wgsl.d.ts +43 -0
  252. package/dist/render/shaders/gpudriven/present.wgsl.js +127 -0
  253. package/dist/render/shaders/gpudriven/shade.wgsl.d.ts +127 -0
  254. package/dist/render/shaders/gpudriven/shade.wgsl.js +874 -0
  255. package/dist/render/shaders/gpudriven/shadow.wgsl.d.ts +44 -0
  256. package/dist/render/shaders/gpudriven/shadow.wgsl.js +308 -0
  257. package/dist/render/shaders/gpudriven/visbufferRaster.wgsl.d.ts +28 -0
  258. package/dist/render/shaders/gpudriven/visbufferRaster.wgsl.js +245 -0
  259. package/dist/render/shaders/network.wgsl.d.ts +101 -0
  260. package/dist/render/shaders/network.wgsl.js +206 -0
  261. package/dist/render/shaders/recon/motion.wgsl.d.ts +44 -0
  262. package/dist/render/shaders/recon/motion.wgsl.js +96 -0
  263. package/dist/render/shaders/recon/resolve.wgsl.d.ts +51 -0
  264. package/dist/render/shaders/recon/resolve.wgsl.js +733 -0
  265. package/dist/render/temporalAa.js +8 -33
  266. package/dist/render/textLayout.js +12 -6
  267. package/dist/render/textRenderer.d.ts +3 -1
  268. package/dist/render/textRenderer.js +4 -1
  269. package/dist/render/translucentQueue.d.ts +6 -0
  270. package/dist/render/translucentQueue.js +11 -0
  271. package/dist/render/windStreakRenderer.d.ts +2 -1
  272. package/dist/render/windStreakRenderer.js +3 -1
  273. package/dist/world/cell.d.ts +63 -0
  274. package/dist/world/cell.js +181 -0
  275. package/dist/world/cellStream.d.ts +103 -0
  276. package/dist/world/cellStream.js +198 -0
  277. package/dist/world/freeze.d.ts +81 -0
  278. package/dist/world/freeze.js +114 -0
  279. package/dist/world/rebase.d.ts +29 -0
  280. package/dist/world/rebase.js +41 -0
  281. package/package.json +8 -3
  282. package/scripts/frames.mjs +92 -0
  283. package/scripts/gpuCompute.mjs +323 -0
  284. package/scripts/png.d.mts +16 -0
  285. package/scripts/png.mjs +25 -0
  286. package/src/core/loop.ts +19 -2
  287. package/src/core/storage.ts +5 -0
  288. package/src/geometry/meshBuilder.ts +5 -1
  289. package/src/index.ts +246 -4
  290. package/src/input/input.ts +6 -1
  291. package/src/math/exact.ts +69 -3
  292. package/src/math/frustum.ts +52 -3
  293. package/src/render/backend/api.ts +42 -1
  294. package/src/render/backend/budget.ts +7 -6
  295. package/src/render/backend/createRenderer.ts +92 -3
  296. package/src/render/backend/materialChanges.ts +54 -0
  297. package/src/render/backend/select.ts +8 -0
  298. package/src/render/backend/webgl2/renderer.ts +211 -9
  299. package/src/render/backend/webgpu/buffers.ts +182 -69
  300. package/src/render/backend/webgpu/fieldCompose.ts +482 -0
  301. package/src/render/backend/webgpu/giFieldPass.ts +200 -0
  302. package/src/render/backend/webgpu/gpuDrivenPass.ts +2993 -0
  303. package/src/render/backend/webgpu/linePass.ts +4 -39
  304. package/src/render/backend/webgpu/probeBake.ts +588 -0
  305. package/src/render/backend/webgpu/renderer.ts +1481 -76
  306. package/src/render/backend/webgpu/scatterPass.ts +15 -1
  307. package/src/render/backend/webgpu/surfaceTexturePass.ts +63 -36
  308. package/src/render/causticsRenderer.ts +5 -2
  309. package/src/render/depthConvention.ts +15 -0
  310. package/src/render/deviceFeatures.ts +77 -0
  311. package/src/render/fog.ts +185 -0
  312. package/src/render/frame/alias.ts +97 -0
  313. package/src/render/frame/deps.ts +155 -0
  314. package/src/render/frame/flushGraph.ts +135 -0
  315. package/src/render/frame/graphSchedule.ts +170 -0
  316. package/src/render/frame/index.ts +52 -0
  317. package/src/render/frame/lifetime.ts +61 -0
  318. package/src/render/frame/passTimings.ts +69 -0
  319. package/src/render/frame/validate.ts +40 -0
  320. package/src/render/frame/virtual.ts +91 -0
  321. package/src/render/gi/chain.ts +225 -0
  322. package/src/render/gi/denoise.ts +276 -0
  323. package/src/render/gi/fieldScene.ts +264 -0
  324. package/src/render/gi/globalField.ts +543 -0
  325. package/src/render/gi/probeGridFit.ts +89 -0
  326. package/src/render/gi/probeTrace.ts +143 -0
  327. package/src/render/gi/probeVolume.ts +288 -0
  328. package/src/render/gi/reflection.ts +163 -0
  329. package/src/render/gi/traceField.ts +223 -0
  330. package/src/render/gi/traceScreen.ts +106 -0
  331. package/src/render/gpuScene.ts +80 -0
  332. package/src/render/gpudriven/alphaTest.ts +33 -0
  333. package/src/render/gpudriven/clusterUpload.ts +123 -0
  334. package/src/render/gpudriven/clusterWorld.ts +239 -0
  335. package/src/render/gpudriven/compact.ts +80 -0
  336. package/src/render/gpudriven/cullClusters.ts +333 -0
  337. package/src/render/gpudriven/decodeTables.ts +332 -0
  338. package/src/render/gpudriven/frustum.ts +70 -0
  339. package/src/render/gpudriven/geometryHarness.ts +51 -0
  340. package/src/render/gpudriven/hzb.ts +113 -0
  341. package/src/render/gpudriven/ibl.ts +147 -0
  342. package/src/render/gpudriven/indirect.ts +89 -0
  343. package/src/render/gpudriven/instances.ts +234 -0
  344. package/src/render/gpudriven/lit.ts +329 -0
  345. package/src/render/gpudriven/lodCut.ts +62 -0
  346. package/src/render/gpudriven/materialBin.ts +131 -0
  347. package/src/render/gpudriven/materialTable.ts +164 -0
  348. package/src/render/gpudriven/pipeline.ts +336 -0
  349. package/src/render/gpudriven/rangeAlloc.ts +122 -0
  350. package/src/render/gpudriven/sceneUpload.ts +173 -0
  351. package/src/render/gpudriven/shadeBins.ts +222 -0
  352. package/src/render/gpudriven/shadow.ts +285 -0
  353. package/src/render/gpudriven/shadowCamera.ts +406 -0
  354. package/src/render/gpudriven/streamScene.ts +575 -0
  355. package/src/render/gpudriven/surfaceFrame.ts +68 -0
  356. package/src/render/gpudriven/twoPhase.ts +214 -0
  357. package/src/render/gpudriven/visbuffer.ts +41 -0
  358. package/src/render/halfFloat.ts +22 -4
  359. package/src/render/inference/attentionTiled.ts +132 -0
  360. package/src/render/inference/denseKernels.ts +369 -0
  361. package/src/render/inference/device.ts +44 -0
  362. package/src/render/inference/deviceGraph.ts +39 -0
  363. package/src/render/inference/fuse.ts +47 -0
  364. package/src/render/inference/kernelKit.ts +120 -0
  365. package/src/render/inference/kernels.ts +18 -0
  366. package/src/render/inference/plan.ts +26 -0
  367. package/src/render/inference/reuse.ts +79 -0
  368. package/src/render/inference/runner.ts +204 -0
  369. package/src/render/inference/schedule.ts +80 -0
  370. package/src/render/inference/shapeKernels.ts +112 -0
  371. package/src/render/inference/spatialKernels.ts +220 -0
  372. package/src/render/inference/splatRaster.wgsl.ts +348 -0
  373. package/src/render/inference/splatTiles.ts +186 -0
  374. package/src/render/lineBatch.ts +4 -24
  375. package/src/render/mesh.ts +46 -1
  376. package/src/render/pass.ts +155 -10
  377. package/src/render/prefilterEnvMap.ts +33 -0
  378. package/src/render/readback.ts +73 -0
  379. package/src/render/recon/clamp.ts +113 -0
  380. package/src/render/recon/disocclusion.ts +143 -0
  381. package/src/render/recon/frameGen.ts +191 -0
  382. package/src/render/recon/frameSizes.ts +64 -0
  383. package/src/render/recon/jitter.ts +126 -0
  384. package/src/render/recon/motionVectors.ts +138 -0
  385. package/src/render/recon/reproject.ts +179 -0
  386. package/src/render/recon/resolve.ts +444 -0
  387. package/src/render/recon/tier.ts +31 -0
  388. package/src/render/reflectionProbe.ts +34 -0
  389. package/src/render/renderQuality.ts +87 -0
  390. package/src/render/screenSpaceReflection.ts +37 -3
  391. package/src/render/sdfFont.ts +2 -0
  392. package/src/render/segmentQuads.ts +39 -0
  393. package/src/render/shaders/flat/lobes.ts +35 -3
  394. package/src/render/shaders/flat/preamble.ts +25 -1
  395. package/src/render/shaders/flat/probeGrid.ts +77 -2
  396. package/src/render/shaders/generated/flat.wgsl.ts +64 -24
  397. package/src/render/shaders/gi/composeField.wgsl.ts +239 -0
  398. package/src/render/shaders/gi/marchField.wgsl.ts +146 -0
  399. package/src/render/shaders/gi/probeBake.wgsl.ts +649 -0
  400. package/src/render/shaders/gi/probeTrace.wgsl.ts +269 -0
  401. package/src/render/shaders/gi/probeVolume.wgsl.ts +246 -0
  402. package/src/render/shaders/gi/sampleField.wgsl.ts +140 -0
  403. package/src/render/shaders/gpudriven/blendRaster.wgsl.ts +343 -0
  404. package/src/render/shaders/gpudriven/compact.wgsl.ts +59 -0
  405. package/src/render/shaders/gpudriven/cull.wgsl.ts +433 -0
  406. package/src/render/shaders/gpudriven/decode.wgsl.ts +373 -0
  407. package/src/render/shaders/gpudriven/environment.wgsl.ts +141 -0
  408. package/src/render/shaders/gpudriven/fog.wgsl.ts +110 -0
  409. package/src/render/shaders/gpudriven/frameBlock.wgsl.ts +62 -0
  410. package/src/render/shaders/gpudriven/materialBin.wgsl.ts +116 -0
  411. package/src/render/shaders/gpudriven/materialTable.wgsl.ts +46 -0
  412. package/src/render/shaders/gpudriven/meshTransform.wgsl.ts +37 -0
  413. package/src/render/shaders/gpudriven/present.wgsl.ts +129 -0
  414. package/src/render/shaders/gpudriven/shade.wgsl.ts +884 -0
  415. package/src/render/shaders/gpudriven/shadow.wgsl.ts +322 -0
  416. package/src/render/shaders/gpudriven/visbufferRaster.wgsl.ts +251 -0
  417. package/src/render/shaders/network.wgsl.ts +238 -0
  418. package/src/render/shaders/recon/motion.wgsl.ts +101 -0
  419. package/src/render/shaders/recon/resolve.wgsl.ts +741 -0
  420. package/src/render/temporalAa.ts +9 -33
  421. package/src/render/textLayout.ts +12 -6
  422. package/src/render/textRenderer.ts +5 -2
  423. package/src/render/translucentQueue.ts +11 -0
  424. package/src/render/windStreakRenderer.ts +4 -2
  425. package/src/world/cell.ts +231 -0
  426. package/src/world/cellStream.ts +278 -0
  427. package/src/world/freeze.ts +157 -0
  428. package/src/world/rebase.ts +56 -0
package/CHANGELOG.json CHANGED
@@ -1,4 +1,115 @@
1
1
  [
2
+ {
3
+ "version": "4.0.0",
4
+ "date": "2026-09-20",
5
+ "entries": [
6
+ {
7
+ "kind": "changed",
8
+ "short": "A video becomes a scene, the engine runs without a browser, and the editor opens. Five new packages, a GPU driven pipeline, worlds past float precision, and 769 public symbols added, none removed.",
9
+ "text": "**This is a major version for what it adds, not for what it breaks.** Five packages are new — `capture`, `native-host`, `nav`, `texture` and `tools` — alongside an `editor` application workspace, bringing the tree to twenty-three packages. Across every package barrel, **769 public symbols were added and none were removed**, so a game built against 3.63.0 compiles against this unchanged.\n\n**What is in it, in one paragraph.** A GPU-driven pipeline behind one option, and a frame graph under the renderer. Temporal reconstruction, and indirect light that falls back through a bounded world-space field rather than guessing off the edge of the screen. Worlds larger than single precision can address, streamed by where the camera is about to look, with terrain that decides its own detail and a navigation mesh across it. Textures as compiled programs over a latent rather than images, with residency predicted instead of reacted to. An editor that opens, scrubs the simulation backwards, and lets a scene be edited while it runs. The engine on a native window with no browser in the process. And a video of a room becoming a scene with colliders, materials and proposed entities, on the player's own device.\n\n**What a consumer owes the upgrade is the bump itself.** A release moves `version` in twenty-five manifests, the sixty-nine internal `@driftengine/*` ranges that pin the packages to each other, and `package-lock.json` — ninety-five places. That count was ninety-three in this repository's own notes until it was counted rather than read, immediately before this release, and answered two more.\n\nNothing here moves the `driftscript` pin, which is a published package on a version line this repository does not own."
10
+ },
11
+ {
12
+ "kind": "added",
13
+ "short": "The editor opens. npm run editor gives a menu bar, a viewport, docked panels, picking, a gizmo, undo and a command palette, on the browser and on a native window.",
14
+ "text": "**`npm run editor` and it is there.** A menu bar, a viewport, docked panel slots, selection by picking, delete, undo, redo, select-all and a command palette. Every edit is a `Command` on a stack, so a panel cannot touch the world except through something the stack can put back.\n\n**`app.ts` is the general shell and `shell.ts` is the product**, which is a deviation from the design and the right one: assembling a product into the reusable shell would make it depend on a selection model. The dock is a binary tree of splits with one panel per region, and `panels/docked.ts` is the single seam that closes a `Panel<W, V>`'s generics and owns the title strip.\n\n**The gizmo is the editor's arithmetic rather than core's, and the reason is a conditioning number.** Core translates by the closest point between the ray and the axis line, which is right and refuses only within `1e-6` of parallel; between that cutoff and a comfortable angle the answer divides by the squared sine, so **two degrees off the axis multiplies a pixel of mouse noise by 820**. A drag here is computed against a plane containing the axis, chosen per drag as the one whose normal most faces the camera: the two candidates are orthogonal, so the conditioning is never worse than **√2** where a fixed choice reaches a billion four times a turn. An axis pointing at the camera has no good plane at all, so the drag is refused rather than invented. Commands carry absolute positions rather than deltas, which is what makes the merge contract's trap harmless: applying an absorbed command twice writes the same position twice, where a delta would move the object twice as far as the pointer and read as a sensitivity bug.\n\n**What it does not do is in `editor/README.md` rather than left to be found**: there are no tabs and no splitter dragging, and nothing paints the gizmo's arms."
15
+ },
16
+ {
17
+ "kind": "added",
18
+ "short": "Play in editor, and a scrubber that runs the simulation backwards. Scrub to any recorded frame and play forward again, frame-for-frame identical to a run that never scrubbed.",
19
+ "text": "**Press play, press stop, and the scene is exactly as it was** — the oldest defect an editor has. `editor/src/pie/session.ts` captures the world before the first step and restores it on stop, checked by a digest of the whole world rather than a spot check of a few fields. Advancing and stepping are different verbs: a loop calls `advancePie` every frame and it does nothing while paused, and a person presses step and gets exactly that many frames and a session still paused. The world arrives through `Snapshotter`, the engine's own seam, so nothing here knows what a world is.\n\n**Scrub the simulation backwards to any recorded frame, and forward again unchanged.** A keyframe every _n_ frames plus the input log between reconstructs any frame exactly, because the simulation is deterministic. The assertion that matters is the second one: scrub back, play forward, and compare against a run that never scrubbed, frame for frame. **A scrubber that perturbs the simulation is worse than none**, because it produces a version of the bug that is not the bug. A frame the ring has dropped is reported as gone and never as the nearest one.\n\n**Change something at frame four hundred and the rest happens differently.** `editDuringPlay.ts` records the edit and re-simulates from its frame rather than applying it on the spot, which is one path instead of two and the only arrangement under which scrubbing away and back keeps the change. The edit is the same `Command` an inspector emits at rest, so it is undoable and there is no second way to write the world. Editor edits stay out of the input log, because an editor command is not something a peer sends.\n\n**And when two runs stop agreeing, the frame and then the component.** `divergence.ts` binary-searches the per-frame fingerprints and `@driftengine/tools` compares the per-component hashes at that frame. Three things are reported as themselves rather than as a number meaning something else: agreement is not frame zero, no overlap is not agreement, and a whole-world difference no recorded component accounts for is said plainly.\n\n**What this cost is the shortest honest summary of the determinism work.** A timeline, a slider, and the rule that an edit during play goes into the log. Snapshots, rewind, input logs, deterministic replay and fingerprints all shipped already, for the netcode."
20
+ },
21
+ {
22
+ "kind": "added",
23
+ "short": "@driftengine/tools, 3,395 gzipped bytes: a shipped game can carry the editor inspector, console, profiler and network panel, with the command stack that makes their edits undoable.",
24
+ "text": "**A game can ship the editor's panels, and a game that does not pays nothing.** `@driftengine/tools` is the inspector, the console, the profiler and the network panel, with the command stack that makes their edits undoable, at **3,395 bytes gzipped**.\n\n**The line is what a panel needs, not what it shows.** A scene tree and an asset browser want a project, and a shipped game has none, so those stayed in the editor. `Panel.route` returns a `Command` or nothing, so an in-game inspector edit goes onto an undo stack without the game arranging anything.\n\nEvery other package's size floor is unchanged by this one existing, which is the argument rather than a claim about tree-shaking."
25
+ },
26
+ {
27
+ "kind": "added",
28
+ "short": "A node graph canvas over three vocabularies: materials compile to a DTEX decode program, particles to the pool options the engine already takes, and behaviour to DriftScript source.",
29
+ "text": "**One editor over three node vocabularies**, in `editor/src/graph/`. Nodes and links with a cycle refused by name and by node; every edit a command, including a node drag, which merges into one undo entry; a canvas that pans and zooms about the pointer.\n\n**The hit test is handed no view at all.** `buildGraphGeometry` turns graph space into screen space once, into arrays, and both the draw and the hit test read those arrays — because the commonest defect in a graph editor is a hit test that disagrees with the draw about the pan or the zoom, and the only way to prevent it for good is to leave no second place that could apply a transform. Automatic arrangement reads the graph and never the current positions, so tidying is not path-dependent.\n\n**A material graph's compile target is a `DTEX` decode program, not a shader** — the fact a reader most needs and least expects. A texture was already defined as a small interpreted decode program, so a material graph is a visual editor for exactly that structure: a graph-authored material and a baked one are indistinguishable downstream, and the graph editor is a debugger for baked materials as a side effect. A node the vocabulary cannot express grows the decode vocabulary, which costs bytes linearly because an operation is data.\n\n**A particle graph compiles to the `ParticlePoolOptions` the engine already takes**, plus the emission the pool has never owned, so a compiled graph is two records rather than a third representation to keep in step. Randomness is a seed and never the global generator. A curve is two numbers, because two is what `ParticlePool` interpolates between, so a preview agrees with the frame. Capacity is derived from rate × life rather than typed in.\n\n**A behaviour graph emits DriftScript source, and DriftScript's own compiler is what judges it.** The graph gets no semantics, no effect analysis and no hot reload of its own, which is what makes a graph and hand-written code the same artefact and lets a project mix them. A graph **can** write a component it never declared, and the real compiler refuses it with DS0288 in the words a person hand-writing that file would get.\n\n**Each vocabulary ships one honest refusal.** `separate` is in the material palette and deliberately does not compile, because splitting a colour into channels is the first node somebody reaches for that this genuinely cannot do; `turbulence` is the particle vocabulary's."
30
+ },
31
+ {
32
+ "kind": "added",
33
+ "short": "A script editor in the editor, backed by the DriftScript language server rather than a second implementation of the language. It works with no client attached at all.",
34
+ "text": "**DriftScript is edited against the language server that already exists, not a second language implementation.** `editor/src/panels/script.ts` is a text view, a diagnostic gutter and a completion popup; every rule about what the language means stays with the compiler, which is the same line the behaviour-graph editor holds and for the same reason.\n\n**It works with no client at all** — a headless build has none and a test has none, so it is a text view before it is anything else.\n\n**The server's view of the buffer lags it by a message**, so a diagnostic arrives describing text already deleted every time somebody types quickly. Ranges are clamped, and one entirely past the end collapses to an empty mark at the end rather than disappearing, because the server has something to say and dropping it loses the only sign that it did. **Nothing awaits the server**: a change notification goes out and the buffer is already updated, and a completion reply arriving after a dismissal is dropped rather than reopening a popup over text somebody went back to typing."
35
+ },
36
+ {
37
+ "kind": "added",
38
+ "short": "@driftengine/capture: a video becomes a scene you can walk around in, on the player device and with no service. Depth and poses, a fused surface, colliders, a navmesh and proposed entities.",
39
+ "text": "**A clip becomes a surface, on the device, with no service.** `@driftengine/capture` takes a `FrameSource` — a video element in a browser, ffmpeg on the native host — chooses frames, runs Depth Anything 3 Small for poses, depth, intrinsics and a confidence together, fuses the views into a truncated signed-distance volume, marches it and decimates the result. Then `collisionMesh` makes it something a `CharacterController` stands on, a navigation mesh bakes from the same surface, `delight` separates what the surface reflects from the light it was photographed under, and `segmentGeometry` and `proposeEntities` cut it into regions and say what each might be.\n\n**The same stages run in both hosts with nothing in the package changed between them.** Measured 2026-09-20 on an RX 9070 XT over six frames of a 1,401-frame 1080p handheld clip: **7.2 s in the browser and 9.05 s on the native host**, of which 3.6 s and 5.5 s are loading the weights. A room came back as 33,060 triangles over 1.08 × 0.70 × 1.08 m, with one navigation polygon and eight entity proposals, in a 1.09 MB file.\n\n**Its maturity is stated rather than implied, stage by stage, because a reconstruction that looks finished and is not costs a consumer more than one that says so.** Geometry reconstructs what a clip saw from where the clip saw it and nothing more. Colliders hold where the capture has a surface, and the capture does not have one everywhere: dropping a controller from a metre above over a three-by-three grid, **three of nine landed on it and six fell through**, which is the reconstruction being patchy rather than the colliders failing. Delighting recovers colour and says how much it trusts it, and on ordinary footage that is not much — a mean confidence of **0.276** on a wooden table and **0.249** on a room. It cannot remove a cast shadow, because a shadowed point is shadowed from every camera and agreement is not evidence of albedo. Entity proposals arrive unlabelled and undecided: a person accepts or rejects each one in the editor, and the file records decisions rather than guesses.\n\n**Three limits ship as limits.** **Six frames is the ceiling on this device** and ten is refused with an invalid pipeline layout. **The metric scale is not verified**: no capture in this record contained an object of known size, so the metres are the model's claim rather than a measurement. And delighting is the most expensive stage by thirty times — **44 s over 16,670 triangles and 179 s over 33,060** — because it ray-casts each vertex against every triangle with no acceleration structure. Colour is per vertex, so its detail ceiling is the mesh rather than the footage.\n\n**Each model definition is held to the upstream's own code on a seeded miniature**, rather than to a picture that looks about right: Depth Anything 3 agrees to **3 parts per million**, Depth Anything V2 to 7.8e-7, MobileSAM to 3.1e-6, SAM 2.1 to 7.4e-6, and OWLv2 to 4.1e-6 with its box prior held bit for bit. An unread weight is refused, so a branch this engine sets aside is set aside by name.\n\n**The package floor is 66,373 gzipped bytes**, built up a stage at a time so the bill is legible: the largest single step is the Gaussian fit at 5,336, and the assembly step's 7,683 is `@driftengine/drft`'s whole writer rather than this package's code.\n\n**No weights are distributed with this engine.** `tools/capture-weights/` fetches each model at the revision its manifest pins, verifies it and converts it; a game that ships one ships it under that model's own licence, and `CREDITS.md` and `NOTICE` carry the attribution. The runtime never fetches."
40
+ },
41
+ {
42
+ "kind": "added",
43
+ "short": "@driftengine/native-host runs the engine on Node, Dawn and SDL with no browser and no engine code changed, and draws the published scenes pixel-identically to Chrome.",
44
+ "text": "**The engine on a native window and a native WebGPU device, with no browser between them.** `@driftengine/native-host` supplies the page a game expects around its canvas — a window, a GPU, events, gamepads, audio — and **no engine code changed to make it work**. A game's whole contract is one export: the manifest's `native.entry` names a module exporting `mount(canvas)`.\n\n**All nine published scenes come back 0 of 776,960 pixels against Chrome**, checked by `npm run native:gate`. Measured on an RX 9070 XT: **730 ms median from spawn to first frame** — 275 ms to reach the game's `mount`, 355 to build a WebGPU renderer and 85 to its first frame — in a **61.3 MB** archive. Of that 355 ms the adapter and the device are 30; the rest is pipelines the binding has no cache for, which Chrome builds warm in 75 ms, and that gap is recorded as an absent capability rather than smoothed over.\n\n**This is what the platform rule was written for.** Nothing under `src/` may call a platform API directly when a consumer might want a different one, and until a second host existed that rule was held by review. Every platform touch this host had to answer was a capability the caller supplies or a page global, never a call buried in the engine. What each host can do that the other cannot is recorded both ways in `docs/CAPABILITIES.md` §2a, the host's side guarded by a `present` block so a capability cannot quietly go.\n\n**Four defects in it were found by a person at the machine and by no gate**, which is worth publishing because the shape recurs. A first key press was reported as an auto-repeat: the code read `(event.repeat ?? 0) !== 0`, which is right for the number the platform's types declare and inverted for the boolean it actually gives, so held keys were discarded while tapped keys still worked. **Two automated checks confirmed it working**, because a replayed key carries the number `0`, which coerces correctly. Two platform handles were closed twice, each ending the process. A keyboard's power-key endpoint enumerated as a gamepad and took the first pad slot; a device with no axes and no hats is not offered as one now.\n\n**And the editor on this host painted its interface after `endFrame`**, which survives on WebGL2 and vanishes on WebGPU: 0 pixels above black after, 1,747,114 before, with one line moved. It was invisible to every capture, because a readback reads the canvas texture and a texture that is never presented still reads back perfectly.\n\nWindows and macOS are not built yet; the Linux target is."
45
+ },
46
+ {
47
+ "kind": "added",
48
+ "short": "@driftengine/texture and the DTEX chunk: a texture as a compiled decode program over a latent rather than an image, streaming by tile with a content hash so identical tiles share a payload.",
49
+ "text": "**A material in the container as a decode program over a latent, rather than as pictures.** `@driftengine/texture` is the interpreter and `drft/src/dtex.ts` is the chunk: the latent grid, the channel specs, the graph's nodes, a small network's shape and weights, and a tile table with a **content hash** per tile so two identical tiles share one payload.\n\n**The chunk carries no semantics.** `@driftengine/drft` has no dependencies and does not know what an operation means, so the types are plain arrays that `@driftengine/texture`'s satisfy structurally — the arrangement `Terrain` already has with `Heightfield` across the physics boundary. **It validates containment, not meaning**: a tile whose bytes run past the payload and a result register no node writes are refused, because those hand a reader arbitrary memory or an uninitialised value. Those guards are unreachable through the writer, so the tests corrupt a written chunk to reach them.\n\n**No per-chunk version**, which is where it departs from its design: the file's version covers the format and a second one is a second thing to keep in step. It is additive, so an older reader skips it by its length and loses only the material it could not have decoded.\n\n**And a texture cannot move the simulation, asserted rather than left true by construction.** A run sampling an animated `DTEX` every tick fingerprints identically to a run sampling nothing, and the same simulation run twice at different wall-clock times fingerprints identically, because `t` is the caller's, from the simulation's clock. A run that reads a wall clock instead is asserted to **diverge**, without which the first two pass for a decoder that reads a clock nobody noticed."
50
+ },
51
+ {
52
+ "kind": "added",
53
+ "short": "Texture streaming that decides residency from where the camera will be, not where it has been. Measured on a scripted flight: 296 late samples predicted against 1,294 reactive.",
54
+ "text": "**Residency is decided by looking at where the camera _will_ be.** The state of the art is reactive by construction — the most widely deployed implementation's own documentation says streaming is reactive by nature, because nothing can know a tile is needed until a frame has already needed it. That is true of an engine that cannot run its simulation forward and put it back. This one does it every frame, for the netcode.\n\n`predictViews` saves, advances _n_ deterministic steps with no rendering, samples which tiles those views want, and restores. **No feedback buffer and no readback**, so it works for passes that could never write feedback. What those views want is `tilesForView`'s answer: each instance inside a predicted view names the level its nearest point asks for, the finer one within an eighth of a level of a boundary, and every coarser one a sampler falls back to, most important first — so the answer at any budget is a prefix of the answer with none.\n\n**Measured on a scripted flight down a corridor of real latents, 452 tiles through a 256-page cache and never more than 102 in a frame: 296 late samples predicted against 1,294 reactive**, and the 296 are exactly the three frames before the first fetch could possibly have landed.\n\n**Mispredicting degrades rather than fails, and that is asserted rather than claimed.** A camera reversing every three frames is what prediction cannot help with, and the cost is a wasted fetch and a late tile, which is what a reactive engine does on every frame. Measured on that flight: **102 late samples predicted against 755 reactive**. The floor of this mechanism is everyone else's ceiling.\n\n**A tile that will not arrive stops being waited for.** A fetch resolving with nothing leaves the tile requested and its in-flight slot occupied forever if nobody handles it, and a handful of those stops streaming entirely with no error anywhere. A missing tile is remembered as missing, a rejection is treated identically, bytes larger than a page are refused rather than truncated, and a fetch landing after its tile was evicted is dropped.\n\n**And the decode can move from per-sample to residency time**, which is the fallback the design named before the risk could arrive: decode once when a tile becomes resident and sample it as an ordinary texture after. That loses procedural and per-sample parameterisation for the affected materials and keeps the compression, the joint channels and the streaming."
55
+ },
56
+ {
57
+ "kind": "added",
58
+ "short": "Runtime-writable textures that survive replay. A scorch mark goes into the input log, so a recording plays back the same burnt wall instead of a clean one.",
59
+ "text": "**A scorch mark is in the input log, so a replay burns the same wall.** Runtime-mutable textures normally break replay: the marks are not part of the simulation and nothing records them, so a recording plays back clean walls and nothing fails at the time.\n\n`overlay/sparse.ts` allocates only the tiles a mark touched and tracks written per texel **and** per channel; `overlay/journal.ts` records every write beside the input. **Rolling back is rebuilding from the journal**, because a write is not invertible — two marks on one texel leave no record of what was underneath. The journal is truncated on rollback, or a replay draws both what happened and what was undone."
60
+ },
61
+ {
62
+ "kind": "added",
63
+ "short": "@driftengine/nav, 8,401 gzipped bytes: geometry becomes walkable convex polygons and a straight line across them. It reverses a refusal that had named its own trigger.",
64
+ "text": "**`@driftengine/nav` ships, at 8,401 bytes gzipped, and it reverses a refusal that named its own trigger.** `core/src/nav/navGraph.ts` states that it is a graph rather than a mesh, that this is a decision rather than a first step, and that a mesh is the row that is still open if a world's walkable space is genuinely a region. A streamed open world is that world.\n\nGeometry becomes a walkable voxel field — two layers under an overhang, which is why it is voxels and not a heightfield — then a watershed partition, contours checked for self-intersection rather than assumed simple, and convex polygons whose adjacency is symmetric. A one-directional build makes paths work in one direction only and nothing says so.\n\n**One A\\* in the repository**: the query builds a `NavGraph` and hands it to core's existing `NavSearch`, then funnels, so open ground is a straight line and not a walk along polygon centres. The graph is not replaced; it stays right for roads, corridors and docking lanes.\n\n**Two limitations ship with a test pinning each.** A region with a free-standing pillar loses the inner loop, so an agent walks through the pillar; and the funnel gives the shortest path through the corridor A\\* chose, 10.75 against an ideal 9.81 on one wall.\n\n**Steering is still refused**, which is the line the reversal did not cross: a path is a function of geometry and an engine can own one, but how a character moves along it is a game's feel."
65
+ },
66
+ {
67
+ "kind": "added",
68
+ "short": "A world bigger than a float. Simulation coordinates stay absolute and double; only rendering rebases, in whole cells, so a replay fingerprint cannot depend on where the camera stood.",
69
+ "text": "**The simulation never rebases; rendering does.** `core/src/world/` holds a sparse cell grid, a render origin that follows the camera in whole cells, and the rule that keeps them apart: simulation coordinates stay absolute and double, because a rebase would change floating-point results and therefore the replay fingerprint — only in sessions that crossed an origin boundary, which is a divergence nobody reproduces on demand.\n\n**The origin moves in whole cells**, since a continuously moving one re-quantises every vertex every frame and the world shimmers: measured over a 220-tick walk at the far end of the world, it moves **75 times rather than 220**. A round trip through render space costs an ulp of a number under five hundred and **does not grow with distance**, where the same coordinate put into single precision is wrong by **exactly one metre** at 2²⁴–2²⁵, because single precision counts in twos there.\n\n**Cells stream by where the camera will look, not only by where it will be.** `cellsInFrustum` walks the box round a predicted view's corners in double precision and keeps a cell unless it lies wholly beyond one plane: 33,554 km out, it tells a sliver a tenth of a metre inside the view from one a tenth outside, which planes held in single precision keep both of.\n\n**Freezing is a simulation decision and unloading is a memory one**, and `world/freeze.ts` keeps them apart on purpose. A cell outside the simulated radius freezes whether or not its contents are resident, and only a frozen cell may be unloaded, so streaming can change where the bytes live and never what the simulation computes. The alternative — unloading causing freezing — makes the simulation depend on how much memory a machine had, which is a divergence that appears on one player's computer and nowhere else. **A frozen cell still contributes to the fingerprint**, from a digest taken when it froze, because a run that agrees only once everything has thawed is a run that disagreed for a while. What it costs is stated rather than discovered: an agent in an unloaded cell does not walk anywhere, does not age and does not finish what it was doing."
70
+ },
71
+ {
72
+ "kind": "added",
73
+ "short": "Terrain decides its own detail per patch in one pass, and a heightfield is stored as a DTEX layer. Collision reads the decoded heights, so the renderer and the query cannot disagree.",
74
+ "text": "**Which squares of the field to draw, at what detail, as one decision per patch.** `terrain/src/clipmap.ts` takes a camera and a patch index and answers, reading the frame and nothing else — which is what lets it move into a compute pass beside the cluster cut rather than be rewritten there.\n\nEach level's block snaps to an **even** patch index, which is the whole of why the levels nest: an even multiple of a level's patch span is a multiple of the next level's, so a finer block's edge always falls on a coarser patch boundary. **The hole a finer level leaves is not fixed at the centre**, which looks wrong and is right: levels re-centre at different moments, so culling by coverage rather than by a central hole is what removes the need for the L-shaped stitching strip the usual implementation carries. Asserted by counting: **65,536 field cells drawn, exactly, at every one of thirty-three camera positions across a coarse patch** — an area equal to the footprint with no cell counted twice is no overlap and no hole, together.\n\n**A heightfield is a `DTEX` layer, and collision reads the decoded numbers rather than the source ones.** Terrain inherits the format's residency, streaming and determinism instead of growing an image path of its own. **The design had the requirement backwards**: it asked that decoded heights match the source within the format's tolerance, which they do and which is not enough — a renderer reading the layer while a query reads the source differ by exactly that tolerance, everywhere, permanently, and a character floating by a fraction of a millimetre on every surface in the world is a defect nobody attributes. So `terrainFromHeightLayer` builds the `Terrain` from the decoded samples and everything reads that one object.\n\nSplat weights decode renormalised, since four weights rounded to eight bits sum to between 0.994 and 1.006, and a shader that trusts the sum shades a whole hillside wrong, smoothly enough to read as lighting."
75
+ },
76
+ {
77
+ "kind": "added",
78
+ "short": "Hierarchical detail, baked offline: a distant block of a city is one coarse mesh and a distant tree is its own picture. A proxy that would not be cheaper is refused rather than shipped.",
79
+ "text": "**A distant block of a city is one coarse mesh, and a distant tree is its own picture.** `assets/src/bake/hlod.ts` builds both offline. The proxy is not a new mesh merger — `coarseLevel.ts` already emits the boundary of an occupancy grid rather than decimating a surface, which is what stops a merge growing triangles through a car's bodywork — so what is new is the grouping and the level schedule.\n\n**A proxy can be refused, and that is the honest half.** A grid-based merge cannot promise fewer triangles than its input, because its output scales with surface area rather than with triangle count, so the half-budget is checked and a group that is already cheap gets `null` instead of something larger than what it replaced.\n\n**The impostor's hard part is its edges.** Colour is dilated past the silhouette and every tile carries a two-texel gutter, because a transparent texel left at the clear colour is what makes a distant tree glow at its atlas seams. Measured on a tree at eight directions a side: **1.0000 silhouette agreement on a baked direction and 0.9083 between two**, since the blend carries no parallax correction and the fix costs the square of the direction count."
80
+ },
81
+ {
82
+ "kind": "added",
83
+ "short": "DriftTR, an analytic temporal reconstruction tier on WebGPU, off by default. At ratio 1.5 the gilded chamber costs 2.42 to 2.49 ms of GPU against 3.08 to 3.15 with it off.",
84
+ "text": "**DriftTR, the analytic tier, ships on WebGPU and is off by default.** `quality.reconstruction` takes a ratio from 1.3 to 2.0, and anything outside it is off. The world draws at the output size divided by that ratio, jittered along a Halton sequence of eight phases for every output pixel a render pixel covers, and a compute resolve accumulates it into the output: history read bicubically where the surface was, clipped to its neighbourhood's variance box in YCoCg, refused where depth, motion or normals say it is another surface, and sharpened inside the range of its neighbours.\n\nCamera motion comes from the frame's depth; a draw that states its previous transform or skin palette is drawn again into a motion target. A contributed pass is handed the frame's jitter through `PrepareContext.jitter`, and the GPU-driven pipeline and splats draw with it.\n\n**Measured on the gilded chamber at 1280 by 607, three runs each: 3.08 to 3.15 ms of GPU off, 2.62 to 2.69 at 1.3, 2.42 to 2.49 at 1.5 and 2.16 at 2.** `recon-parity.mjs` holds the resolve's WGSL to its reference over seven frames, `ghost-check.mjs` measures a moving object under a still camera and `pan-check.mjs` a moving camera over a still world, and **a replay fingerprints the same with it on at every ratio as with it off**.\n\nA multisampled frame is not reconstructed, and says so once. The learned tier and frame generation are not in this release and are recorded as absent."
85
+ },
86
+ {
87
+ "kind": "added",
88
+ "short": "A GPU-driven pipeline behind createRenderer({ pipeline: gpu-driven }), opt-in and WebGPU only. It refuses on a backend without indirect draws rather than falling back quietly.",
89
+ "text": "**The GPU-driven pipeline is opt-in, WebGPU only, and refuses rather than falls back.** `createRenderer({ pipeline: 'gpu-driven' })` on a backend without indirect draws throws with the reason in words, checked against the backend that will _draw_ rather than the one that was asked for — three of the paths into that decision arrive having already fallen back from WebGPU.\n\nInstance culling, a level cut, a depth reduction, cluster culling, a near-plane refusal, compaction into one draw, a visibility buffer, material binning, surface reconstruction, the lit expression, the shadow lookup, the gradient a compute invocation has to assemble for itself, the octahedral probe mapping, the DriftTexture decode interpreter and the surface frame a textured pixel is read with, and the network evaluator in single and half precision.\n\n**Every pass is written twice** — once in TypeScript as the reference and once in WGSL — and `scripts/gpu-parity.mjs` runs twenty checks requiring the two to agree on a real device. **Until 2026-09-17 that was one short**: the instance cull was checked on the device and never dispatched by the pass, so every cluster of a mesh wholly outside the view was cut and tested one by one. It is a stage of the frame now, and the eighteen scenes are 0 of 921,600 pixels against the build before it.\n\n**Its scene streams, and keeps no copy of what it uploads.** The pass first built its scene in a method whose constructor said the geometry does not move — and a voxel world streams chunks as a player walks and remeshes one on every block they break. Vertices and indices now go through a `GeometrySink` straight into the device's buffers when a mesh is placed, because holding them as well doubled what a world costs and, at the voxel sandbox's radius 32, the copy was an `ArrayBuffer` Chrome will not allocate. **There is one path and not two**: the static case is a scene filled once and never emptied, asserted to pack byte for byte the way the old builder did, because two paths would mean the draft rigs exercise the path no published scene uses, which is how a control stops being a control.\n\n**Timings are a range rather than a number, because the device clock is quantised.** Four captures of each rig land on multiples of about 0.065 ms and nothing between them, so a single frame's figure is a bucket and quoting one is quoting the low end of a distribution."
90
+ },
91
+ {
92
+ "kind": "added",
93
+ "short": "Indirect light as three levels that fall back to each other and never off the end: the screen, a world-space distance field, and a probe volume. CPU-side, and no scene draws differently.",
94
+ "text": "**Indirect light, as three levels that fall back to each other and never off the end.** `ROADMAP.md` refused screen-space global illumination because its error is unbounded: a ray that leaves the frame has no answer, and every technique that ships one anyway invents one from whatever happened to be on screen. **This does not reverse that refusal.** The screen is an accelerator with a world-space distance field behind it, whose error is bounded by its own resolution, and a probe volume behind _that_, which is never wrong and only ever coarse.\n\n`traceIndirect` takes a ray and a `GiResources` and answers from the best level that can; `GI_SOURCE_SCREEN`, `GI_SOURCE_FIELD` and `GI_SOURCE_PROBES` say which one did, so a caller can see the fallback rather than infer it. A scene that wants indirect light and declared no grid is given one that fits it.\n\n**It is CPU-side, and reached by nobody who does not ask.** There is no WGSL, no pass and no renderer option yet, so **no scene renders differently for these existing** — measured rather than asserted: the eight published scenes are 0 of 921,600 pixels against the build before them, and `core-only` is byte-identical with these exported and without them. It is exported anyway, because a capability with no route out of its package is one the first consumer finds by failing a boundary test."
95
+ },
96
+ {
97
+ "kind": "added",
98
+ "short": "A frame graph under the renderer: a recording arena, a resource table, a scheduler and a replay, none of which import a backend, so the scheduling is proven by assertion rather than a picture.",
99
+ "text": "**The frame's pure half.** `core/src/render/frame/` holds the recording arena, the resource table, the scheduler and the replay, and **imports neither backend** — which is what lets the scheduling be proven by assertion rather than by a picture.\n\nA node declares what it touches, by bit where a mask has bits and by identifier when it has run out of them, and a transient resource knows when it is born and when nothing needs it again. `FrameResource` and `PassTimings` are the public surface; the rest stays under `render/`, because nothing outside it may reach in yet and saying so is cheaper than discovering a consumer who did."
100
+ },
101
+ {
102
+ "kind": "added",
103
+ "short": "A compute shader can be tested. gpuCompute.mjs runs WGSL on a real device and reads the buffers back, from a loopback server, because navigator.gpu is not exposed on about:blank.",
104
+ "text": "**`packages/core/scripts/gpuCompute.mjs` runs WGSL on a real device and reads the buffers back**, which is what lets the GPU-driven pipeline's arithmetic have a test at all. Before it, the WGSL half had nothing but a generator check that confirms a committed file matches its source — which says nothing about whether the arithmetic is right.\n\n**It serves its own page from a loopback server**, because `navigator.gpu` is not exposed on `about:blank` and `http://localhost` is a secure context. There is no dev server to start first.\n\n`scripts/gpu-parity.mjs` runs the passes over generated input and requires agreement: **256 spheres with 149 culled, 256 clusters with 58 selected, and a 37×21 depth pyramid reduced to 18×10** — an odd size on purpose, because the extra row and column are what a reduction gets wrong. Both answers must occur in each case, or the agreement is between two constants. **The check was audited by breaking the WGSL five ways** — the reversed-Z minimum, the conservative direction of the cull, the second half of the cut rule, the plane normalisation and the odd-row fold — and it caught all five."
105
+ },
106
+ {
107
+ "kind": "note",
108
+ "short": "What 4.0.0 does not do, stated as deliberately as what it does: no texture atlas or metric anchor on a capture, no learned reconstruction tier, and a proof suite deferred out of this release.",
109
+ "text": "**This project treats an absence as a deliverable**, and `docs/CAPABILITIES.md` §2 names a sentinel symbol per absent row — the symbol that would exist if the work had landed — so the suite goes red on the day one arrives rather than the documentation going quietly stale.\n\n**DriftCapture's three.** There is no texture atlas: colour is one value per vertex, so the detail ceiling is the mesh rather than the footage. There is no delighting prior, so a grey object on a grey floor has no chromaticity to tell shading from paint. And there is no metric anchor: no clip in this record contained an object of known size, so the scale is the model's claim rather than a measurement.\n\n**Reconstruction ships its analytic tier only.** The learned tier and frame generation inside the frame are not here.\n\n**Screen-space global illumination stays refused in writing**, for the reason given when it was first refused: its error is unbounded. What ships instead falls back through a bounded world-space field to a probe volume.\n\n**A proof suite was planned for this release and deferred out of it**, deliberately, so that what is in 4.0.0 is what has been run rather than what was scheduled.\n\n**Two things in this release have been used by one person and no more**: the editor, which nobody who did not write it has opened, and the native host on Windows and macOS, which is not built at all. The Linux target is."
110
+ }
111
+ ]
112
+ },
2
113
  {
3
114
  "version": "3.63.0",
4
115
  "date": "2026-09-14",
package/NOTICE CHANGED
@@ -7,3 +7,23 @@ This product includes software developed at Drift Technologies
7
7
  Licensed under the Apache License, Version 2.0. Section 4(d) of that licence
8
8
  requires this NOTICE to be reproduced in any derivative work you distribute,
9
9
  in the source, the documentation, or a display generated by the work.
10
+
11
+ Models run by DriftCapture
12
+ --------------------------
13
+ DriftCapture runs the models below. Their weights are not in this repository
14
+ and are not distributed with it: tools/capture-weights/ fetches each at the
15
+ revision its manifest pins, verifies it, and converts it, and a game that
16
+ ships one ships it under that model's own licence. Each model's definition in
17
+ this engine is written from its project's published code.
18
+
19
+ - Depth Anything 3 Small and Depth Anything 3 Base. Apache License 2.0.
20
+ Copyright (c) 2025 ByteDance Ltd. and/or its affiliates.
21
+ - Depth Anything V2 Small. Apache License 2.0. The University of Hong Kong
22
+ and TikTok; converted to the Transformers layout by The HuggingFace Team.
23
+ - MobileSAM. Apache License 2.0. Its image encoder is TinyViT, Copyright (c)
24
+ 2022 Microsoft; its prompt encoder and mask decoder are Segment Anything's,
25
+ Copyright (c) Meta Platforms, Inc. and affiliates.
26
+ - SAM 2.1 tiny. Apache License 2.0. Copyright (c) Meta Platforms, Inc. and
27
+ affiliates.
28
+ - OWLv2 base patch16. Apache License 2.0. Copyright 2023 Google AI and The
29
+ HuggingFace Team.
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  The runtime everything needs: renderer, loop, camera, input, geometry. Collision and dynamics live in [`@driftengine/physics`](../physics/README.md), which this package depends on and re-exports whole, so reaching for them through this barrel works as it always did.
4
4
 
5
- **Cost: 670.0 KB gzipped, importing `createRenderer`.** Measured by `scripts/size-gate.test.mjs`, which fails if it drifts more than
5
+ **Cost: 698.4 KB gzipped, importing `createRenderer`.** Measured by `scripts/size-gate.test.mjs`, which fails if it drifts more than
6
6
  3% — the number is a fact about the build rather than a claim in a document.
7
7
 
8
8
  Part of [DriftEngine](../../README.md). See [`docs/ARCHITECTURE.md`](../../docs/ARCHITECTURE.md)
package/dist/core/loop.js CHANGED
@@ -3,7 +3,19 @@ import { framesHeld } from './bootGate.js';
3
3
  export function startLoop(hooks, options = {}) {
4
4
  const fixedDt = options.fixedDt ?? 1 / 60;
5
5
  const maxFrameTime = options.maxFrameTime ?? 0.25;
6
- let last = performance.now();
6
+ /**
7
+ * When the last frame arrived, on the timebase the frames themselves use.
8
+ *
9
+ * **Null until the first frame whenever a source was supplied, because that source owns the
10
+ * timebase and this loop has no way to ask what it is.** Seeding from `performance.now()`
11
+ * regardless is what this did, and for the window it is right — a `DOMHighResTimeStamp` and
12
+ * `performance.now()` share an origin. For anything else it mixes two clocks: the synthetic
13
+ * session in `@driftengine/xr` counts from its own zero, so the first delta became sixteen
14
+ * milliseconds minus a process uptime, the accumulator went as negative as that, and the
15
+ * simulation stopped ticking until it climbed back — one dead frame per sixteen milliseconds the
16
+ * process had been alive. A caller that supplies the frames supplies the timebase.
17
+ */
18
+ let last = options.frameSource === undefined ? performance.now() : null;
7
19
  let accumulator = 0;
8
20
  /**
9
21
  * Fixed steps taken, offset by `startTick`.
@@ -23,13 +35,17 @@ export function startLoop(hooks, options = {}) {
23
35
  * another, because the two run at different rates and share no accumulator worth carrying across.
24
36
  */
25
37
  const source = options.frameSource ?? {
38
+ // platform: browser default — `LoopOptions.frameSource` is the seam
26
39
  requestAnimationFrame: (callback) => requestAnimationFrame(callback),
27
40
  cancelAnimationFrame: (handle) => {
41
+ // platform: browser default — as above
28
42
  cancelAnimationFrame(handle);
29
43
  },
30
44
  };
31
45
  const frame = (now, xrFrame) => {
32
46
  rafId = source.requestAnimationFrame(frame);
47
+ /* The first frame of a supplied source establishes the origin, so it advances nothing. */
48
+ const previous = last ?? now;
33
49
  /*
34
50
  * **Held: draw nothing, advance nothing, and bank nothing.**
35
51
  *
@@ -48,7 +64,7 @@ export function startLoop(hooks, options = {}) {
48
64
  accumulator = 0;
49
65
  return;
50
66
  }
51
- let frameDt = (now - last) / 1000;
67
+ let frameDt = (now - previous) / 1000;
52
68
  last = now;
53
69
  const wallDt = frameDt;
54
70
  if (frameDt > maxFrameTime)
@@ -35,6 +35,7 @@ export class BrowserStore {
35
35
  if (!this.available)
36
36
  return this.fallback.read(key);
37
37
  try {
38
+ // platform: browser default — `KeyValueStore` is the seam; see `MemoryStore` beside it
38
39
  return localStorage.getItem(key);
39
40
  }
40
41
  catch {
@@ -46,6 +47,7 @@ export class BrowserStore {
46
47
  if (!this.available)
47
48
  return;
48
49
  try {
50
+ // platform: browser default — as above
49
51
  localStorage.setItem(key, value);
50
52
  }
51
53
  catch {
@@ -57,6 +59,7 @@ export class BrowserStore {
57
59
  if (!this.available)
58
60
  return;
59
61
  try {
62
+ // platform: browser default — as above
60
63
  localStorage.removeItem(key);
61
64
  }
62
65
  catch {
@@ -69,7 +72,9 @@ function probeLocalStorage() {
69
72
  // Reading is not enough of a probe: some browsers allow the read and throw
70
73
  // only on write, which would strand a store that reports itself healthy.
71
74
  const probe = '__driftengine_probe__';
75
+ // platform: feature probe — a blocked store must be found by trying it
72
76
  localStorage.setItem(probe, '1');
77
+ // platform: feature probe — as above
73
78
  localStorage.removeItem(probe);
74
79
  return true;
75
80
  }
@@ -332,7 +332,7 @@ export declare class MeshBuilder {
332
332
  */
333
333
  addBlob(center: Vec3, radiusAt: (u: number, v: number) => number, color: Vec3 | ((u: number, v: number) => Vec3), emissive?: number, segments?: number, rings?: number, specular?: number): this;
334
334
  /** A sphere, which is a capsule with no shaft. */
335
- addSphere(center: Vec3, radius: number, color: Vec3, emissive?: number, segments?: number, rings?: number): this;
335
+ addSphere(center: Vec3, radius: number, color: Vec3, emissive?: number, segments?: number, rings?: number, specular?: number): this;
336
336
  /** Add a faceted cylinder aligned to one principal axis. */
337
337
  addCylinder(center: Vec3, radius: number, halfLength: number, axis: 'x' | 'y' | 'z', color: Vec3, emissive?: number, segments?: number, specular?: number): this;
338
338
  /**
@@ -678,8 +678,12 @@ export class MeshBuilder {
678
678
  return this;
679
679
  }
680
680
  /** A sphere, which is a capsule with no shaft. */
681
- addSphere(center, radius, color, emissive = 0, segments = 12, rings = 6) {
682
- return this.addCapsule(center, radius, 0, color, emissive, segments, rings);
681
+ addSphere(center, radius, color, emissive = 0, segments = 12, rings = 6,
682
+ /* `addCapsule` has taken one since it existed; forwarding it is what was missing, and a
683
+ sphere that cannot carry the specular a box can is the reason no page here could draw a
684
+ polished surface under a direct light. */
685
+ specular = 0) {
686
+ return this.addCapsule(center, radius, 0, color, emissive, segments, rings, specular);
683
687
  }
684
688
  /** Add a faceted cylinder aligned to one principal axis. */
685
689
  addCylinder(center, radius, halfLength, axis, color, emissive = 0, segments = 12, specular = 0) {
package/dist/index.d.ts CHANGED
@@ -5,7 +5,7 @@ export type { LoopHooks, FrameSource, LoopOptions } from './core/loop.ts';
5
5
  export { StepBudget } from './core/stepBudget.ts';
6
6
  export type { StepBudgetOptions } from './core/stepBudget.ts';
7
7
  export { hashToUnit, mulberry32, pickBySeed, savableMulberry32 } from './core/rng.ts';
8
- export { exactAcos, exactCos, exactExp, exactSin } from './math/exact.ts';
8
+ export { exactAcos, exactCos, exactExp, exactLog, exactSin } from './math/exact.ts';
9
9
  export type { SavableRandom } from './core/rng.ts';
10
10
  export { TickTrace } from './core/tickTrace.ts';
11
11
  export type { TickTraceLike } from './core/tickTrace.ts';
@@ -54,8 +54,8 @@ export { PickableSet } from './render/pickable.ts';
54
54
  export type { PickHit, PickableSource } from './render/pickable.ts';
55
55
  export { boundsOfBox, boundsOfPositions, createBounds } from './math/bounds.ts';
56
56
  export type { Bounds } from './math/bounds.ts';
57
- export { createFrustum, frustumFromViewProjection, sphereInFrustum } from './math/frustum.ts';
58
- export type { Frustum } from './math/frustum.ts';
57
+ export { boxInFrustum, createFrustum, frustumFromViewProjection, sphereInFrustum, } from './math/frustum.ts';
58
+ export type { Frustum, FrustumPlanes } from './math/frustum.ts';
59
59
  export { boundsVisible } from './render/visibility.ts';
60
60
  export { apparentSize, lodForBounds } from './render/lod.ts';
61
61
  export { SceneNode } from './scene/node.ts';
@@ -66,12 +66,67 @@ export type { PassContext, PassDefinition, PassDevice, PassHandle, PrepareContex
66
66
  export type { PassAttachmentOptions, PassTarget } from './render/passTarget.ts';
67
67
  export { generateMipChain, mipLevelCount, type MipPipelines, } from './render/backend/webgpu/surfaceTexturePass.ts';
68
68
  export { createPassAttachment } from './render/passTarget.ts';
69
+ export { jitterClip } from './render/recon/jitter.ts';
69
70
  export type { FrameResource } from './render/frame/index.ts';
70
71
  export type { OcclusionOptions } from './render/occlusion.ts';
71
72
  export { OcclusionBuffer } from './render/occlusion.ts';
72
73
  export type { ComputeContext, ComputeDefinition, ComputeDevice, ComputeHandle, } from './render/compute.ts';
73
74
  export { createRenderer } from './render/backend/createRenderer.ts';
74
- export type { CreatedRenderer, CreateRendererOptions } from './render/backend/createRenderer.ts';
75
+ export type { CreatedRenderer, CreateRendererOptions, RenderPipeline, } from './render/backend/createRenderer.ts';
76
+ export { GPU_DRIVEN_LISTS, GpuDrivenPass, MATERIAL_FLOATS, } from './render/backend/webgpu/gpuDrivenPass.ts';
77
+ export type { GpuDrivenList, GpuDrivenMaterial, GpuDrivenShadowOptions, GpuDrivenView, } from './render/backend/webgpu/gpuDrivenPass.ts';
78
+ export { atmosphereFog, createFogTarget } from './render/fog.ts';
79
+ export type { FogOptions, FogTarget } from './render/fog.ts';
80
+ export { buildGpuDrivenScene } from './render/gpudriven/sceneUpload.ts';
81
+ export type { GpuDrivenMesh, GpuDrivenScene } from './render/gpudriven/sceneUpload.ts';
82
+ export { StreamingScene, streamingScene } from './render/gpudriven/streamScene.ts';
83
+ export type { DirtySpan, GeometrySink, StreamCapacity, StreamHandle, StreamUpload, } from './render/gpudriven/streamScene.ts';
84
+ export type { ClusterSource } from './render/gpudriven/clusterUpload.ts';
85
+ export { packDecodeTables, programFromEncoded } from './render/gpudriven/decodeTables.ts';
86
+ export type { DecodeTables, EncodedProgramShape, GpuDrivenLatent, GpuDrivenNetwork, GpuDrivenProgram, } from './render/gpudriven/decodeTables.ts';
87
+ export type { GpuDrivenTextures } from './render/gpudriven/materialTable.ts';
88
+ export { openInferenceDevice } from './render/inference/device.ts';
89
+ export type { InferenceDevice } from './render/inference/device.ts';
90
+ export { createGraphRunner } from './render/inference/runner.ts';
91
+ export { planReuse } from './render/inference/reuse.ts';
92
+ export { countSplatTiles, fillSplatTiles, splatPixelBox, splatTileGrid, splatTileOffsets, SPLAT_BIN_FLOATS, SPLAT_TILE, } from './render/inference/splatTiles.ts';
93
+ export type { BufferReuse } from './render/inference/reuse.ts';
94
+ export type { GraphRunner } from './render/inference/runner.ts';
95
+ export type { DeviceAttribute, DeviceGraph, DeviceGraphNode, } from './render/inference/deviceGraph.ts';
96
+ /**
97
+ * **Indirect light, as three levels that fall back to each other and never off the end.**
98
+ *
99
+ * `ROADMAP.md` refused screen-space global illumination because its error is unbounded — a ray
100
+ * that leaves the frame has no answer, and every technique that ships one anyway invents one from
101
+ * whatever happened to be on screen. This does not reverse that refusal: the screen is an
102
+ * accelerator with a world-space distance field behind it, whose error is bounded by its own
103
+ * resolution, and a probe volume behind *that*, which is never wrong and only ever coarse.
104
+ *
105
+ * **CPU-side, and reached by nobody who does not ask.** There is no WGSL, no pass and no renderer
106
+ * option yet, so no scene renders differently for these existing — this wave's first constraint,
107
+ * measured rather than asserted: the eight published scenes are 0 of 921,600 pixels against the
108
+ * build before them, and `core-only` is byte-identical with these exported and without them.
109
+ * Exported anyway, for the reason `bake/cluster.ts` was not: a capability with no route out of its
110
+ * package is one the first consumer finds by failing a boundary test.
111
+ */
112
+ export { GI_SOURCE_FIELD, GI_SOURCE_PROBES, GI_SOURCE_SCREEN, newIndirectResult, traceIndirect, } from './render/gi/chain.ts';
113
+ export type { GiResources, GiSource, IndirectRay, IndirectResult } from './render/gi/chain.ts';
114
+ export { GLOBAL_FIELD_BLEND, composeGlobalField, createGlobalField, sampleGlobalField, } from './render/gi/globalField.ts';
115
+ export type { FieldSource, GlobalField, GlobalFieldCascade, GlobalFieldInstance, } from './render/gi/globalField.ts';
116
+ export { GI_SCREEN_MARCH, SCREEN_TRACE_BIAS_M, screenRayOrigin, traceScreen, } from './render/gi/traceScreen.ts';
117
+ export { FIELD_MARCH, coneRadiusAt, newFieldHit, traceField } from './render/gi/traceField.ts';
118
+ export type { FieldHit, FieldMarch } from './render/gi/traceField.ts';
119
+ export { PROBE_VISIBILITY_SHARPNESS, bakeProbeVisibility, createProbeVisibility, probeUpdateSchedule, probeVisibilityWeight, sampleProbeVolume, visibleProbes, } from './render/gi/probeVolume.ts';
120
+ export type { ProbeVisibility } from './render/gi/probeVolume.ts';
121
+ export { DENOISE_MIN_ALPHA, filterStepFor, newTemporalPixel, spatialDenoise, temporalDenoise, } from './render/gi/denoise.ts';
122
+ export type { DenoiseFrame, SpatialOptions, TemporalPixel } from './render/gi/denoise.ts';
123
+ export { reflectDirection, reflectionCone, traceReflection } from './render/gi/reflection.ts';
124
+ export { GiFieldPass } from './render/backend/webgpu/giFieldPass.ts';
125
+ export type { GiFieldOptions } from './render/backend/webgpu/giFieldPass.ts';
126
+ export { DistanceFieldScene, MAX_DISTANCE_FIELDS } from './render/gi/fieldScene.ts';
127
+ export type { DistanceFieldInstance } from './render/gi/fieldScene.ts';
128
+ export { placeCascade } from './render/gi/globalField.ts';
129
+ export type { ReflectionOptions, ReflectiveSample } from './render/gi/reflection.ts';
75
130
  export { GRADE_PLACEHOLDER_SIZE, MAX_GRADE_SIZE, identityGradeLut } from './render/colourGrade.ts';
76
131
  export type { ColourGradeLut } from './render/colourGrade.ts';
77
132
  export { probeDevice } from './render/backend/probe.ts';
@@ -195,6 +250,7 @@ export type { ColliderSurfaceOptions } from './physics/colliderSurface.ts';
195
250
  export { BoxSurface } from './physics/boxSurface.ts';
196
251
  export { heightSurface } from './physics/heightSurface.ts';
197
252
  export { DebugLines } from './render/debugLines.ts';
253
+ export { rayClosestOnLine, rayPlane, raySphere } from './math/intersect.ts';
198
254
  export { GIZMO_GROUP_ACTIVE, GIZMO_GROUP_COLORS, GIZMO_GROUP_COUNT, GIZMO_GROUP_NEUTRAL, GIZMO_GROUP_X, GIZMO_GROUP_Y, GIZMO_GROUP_Z, GIZMO_NONE, GIZMO_ROTATE_X, GIZMO_ROTATE_Y, GIZMO_ROTATE_Z, GIZMO_SCALE_UNIFORM, GIZMO_SCALE_X, GIZMO_SCALE_Y, GIZMO_SCALE_Z, GIZMO_TRANSLATE_X, GIZMO_TRANSLATE_XY, GIZMO_TRANSLATE_Y, GIZMO_TRANSLATE_YZ, GIZMO_TRANSLATE_Z, GIZMO_TRANSLATE_ZX, Gizmo, gizmoScaleFor, } from './render/gizmo.ts';
199
255
  export type { GizmoMode, GizmoSpace } from './render/gizmo.ts';
200
256
  export { RainField } from './render/rainField.ts';
@@ -253,6 +309,8 @@ export { CONTACT_LIMIT, createContactReports, describeContacts } from './dev/con
253
309
  export type { ContactReport } from './dev/contactProbe.ts';
254
310
  export { GPU_SLOTS, GpuTimer } from './render/gpuTimer.ts';
255
311
  export type { GpuSample, GpuSlot } from './render/gpuTimer.ts';
312
+ export { createPassTimings, passLabel, passMs, recordPassLabel, recordPassSample, resetPassTimings, } from './render/frame/passTimings.ts';
313
+ export type { PassTimings } from './render/frame/passTimings.ts';
256
314
  export { classifyRightGesture } from './input/rightGesture.ts';
257
315
  export type { RightGesture, RightGestureOptions } from './input/rightGesture.ts';
258
316
  export { createWindState, sampleWind } from './render/wind.ts';
@@ -288,3 +346,10 @@ export type { TreeParams, TreeGeometry } from './geometry/treeBuilder.ts';
288
346
  export type { InstanceData } from './render/instancedMesh.ts';
289
347
  export type { MeshInstances } from './render/instances.ts';
290
348
  export type { WindProfile, WindState } from './render/wind.ts';
349
+ export { cellBounds, cellCoord, cellCoordInRange, cellCoordsOf, cellIdFor, cellIdFrom, cellsInFrustum, cellsInRadius, createCellGrid, } from './world/cell.ts';
350
+ export type { CellGrid } from './world/cell.ts';
351
+ export { renderOrigin, toRenderSpace, toWorldSpace } from './world/rebase.ts';
352
+ export { cameraPositionFrom, cellPredictor, createCellStream, pumpCellStream, setCellStreamOrigin, } from './world/cellStream.ts';
353
+ export type { CellStore, CellStream, CellStreamOptions } from './world/cellStream.ts';
354
+ export { createFrozenCells, freezeCell, frozenEntities, frozenFingerprintContribution, isCellFrozen, thawCell, } from './world/freeze.ts';
355
+ export type { FreezableWorld, FrozenCell, FrozenCells } from './world/freeze.ts';
package/dist/index.js CHANGED
@@ -3,7 +3,7 @@
3
3
  export { startLoop } from './core/loop.js';
4
4
  export { StepBudget } from './core/stepBudget.js';
5
5
  export { hashToUnit, mulberry32, pickBySeed, savableMulberry32 } from './core/rng.js';
6
- export { exactAcos, exactCos, exactExp, exactSin } from './math/exact.js';
6
+ export { exactAcos, exactCos, exactExp, exactLog, exactSin } from './math/exact.js';
7
7
  export { TickTrace } from './core/tickTrace.js';
8
8
  export { BrowserStore, MemoryStore, defaultStore } from './core/storage.js';
9
9
  /* The same seam over a backend that answers later, with the queue, the retry and the status. */
@@ -60,7 +60,7 @@ export { MAX_DEPTH_LAYER, Renderer, WebGL2Renderer } from './render/backend/webg
60
60
  export { PickableSet } from './render/pickable.js';
61
61
  /* How big a piece of geometry is, which everything that culls or picks a detail level starts from. */
62
62
  export { boundsOfBox, boundsOfPositions, createBounds } from './math/bounds.js';
63
- export { createFrustum, frustumFromViewProjection, sphereInFrustum } from './math/frustum.js';
63
+ export { boxInFrustum, createFrustum, frustumFromViewProjection, sphereInFrustum, } from './math/frustum.js';
64
64
  export { boundsVisible } from './render/visibility.js';
65
65
  export { apparentSize, lodForBounds } from './render/lod.js';
66
66
  /* A transform hierarchy. Additive: nothing in the renderer knows a node exists. */
@@ -79,6 +79,11 @@ export { createVisitResult, visitVisible } from './scene/visit.js';
79
79
  */
80
80
  export { generateMipChain, mipLevelCount, } from './render/backend/webgpu/surfaceTexturePass.js';
81
81
  export { createPassAttachment } from './render/passTarget.js';
82
+ /*
83
+ * What a pass drawing the world does with `PrepareContext.jitter`, so a reconstructed frame's
84
+ * resolve finds its geometry where the renderer's own verbs put theirs.
85
+ */
86
+ export { jitterClip } from './render/recon/jitter.js';
82
87
  export { OcclusionBuffer } from './render/occlusion.js';
83
88
  /*
84
89
  * `Renderer` above stays exported and is not deprecated. `createRenderer` is the way to get
@@ -87,6 +92,80 @@ export { OcclusionBuffer } from './render/occlusion.js';
87
92
  * take one.
88
93
  */
89
94
  export { createRenderer } from './render/backend/createRenderer.js';
95
+ /*
96
+ * The second pipeline, as the two things a consumer assembles: the scene packing and the pass
97
+ * that draws it.
98
+ *
99
+ * **Exported here and reached by nobody who does not ask for it.** `pipeline: 'gpu-driven'` on
100
+ * `createRenderer` is the permission — it fails at boot where the backend cannot run this — and
101
+ * `GpuDrivenPass` is the implementation, registered the way `@driftengine/splats` registers one.
102
+ * A consumer who names neither pays nothing: `core-only` is measured with these exported and does
103
+ * not move, because nothing in the forward path reaches them.
104
+ *
105
+ * It draws vertex colour, one directional term, a hemispheric ambient and an emissive add — not
106
+ * the standard material. `DRAFT_SCENES`, never `SCENES`, until it draws the six published ones.
107
+ */
108
+ export { GPU_DRIVEN_LISTS, GpuDrivenPass, MATERIAL_FLOATS, } from './render/backend/webgpu/gpuDrivenPass.js';
109
+ /*
110
+ * **One environment, one haze, on both pipelines.** `GpuDrivenView.fog` is the medium the forward
111
+ * path binds, and a consumer drawing both in one frame fills it from `atmosphereFog` rather than
112
+ * working the numbers out again — the voxel sandbox's port is the first.
113
+ */
114
+ export { atmosphereFog, createFogTarget } from './render/fog.js';
115
+ export { buildGpuDrivenScene } from './render/gpudriven/sceneUpload.js';
116
+ /*
117
+ * **The scene a `GpuDrivenPass` takes**, which is a streaming one since 2026-09-18 — a static
118
+ * scene is one filled once, so there is a single path rather than two. `buildGpuDrivenScene` stays
119
+ * exported because it is the packer `streamScene.test.ts` holds the streaming layout to.
120
+ */
121
+ export { StreamingScene, streamingScene } from './render/gpudriven/streamScene.js';
122
+ export { packDecodeTables, programFromEncoded } from './render/gpudriven/decodeTables.js';
123
+ /*
124
+ * **A network graph on a device**: the one neural runtime's device half. `@driftengine/texture`
125
+ * holds the references and validates a graph; this runs the validated graph, whose shapes it takes
126
+ * as given, on a device opened from whichever `GPU` the caller has — a browser's or the native
127
+ * host's Dawn. Exported because the device may not leave `render/`, so a package that runs a
128
+ * network does so through here or not at all.
129
+ */
130
+ export { openInferenceDevice } from './render/inference/device.js';
131
+ export { createGraphRunner } from './render/inference/runner.js';
132
+ /* The buffer-reuse rule, exported because `@driftengine/texture`'s evaluator plans by this one
133
+ rather than by a second copy of it — see `reuse.ts` for why it is on this side of the arrow. */
134
+ export { planReuse } from './render/inference/reuse.js';
135
+ /* The tile lists a device draws a splat cloud from, beside the shader that walks them. */
136
+ export { countSplatTiles, fillSplatTiles, splatPixelBox, splatTileGrid, splatTileOffsets, SPLAT_BIN_FLOATS, SPLAT_TILE, } from './render/inference/splatTiles.js';
137
+ /**
138
+ * **Indirect light, as three levels that fall back to each other and never off the end.**
139
+ *
140
+ * `ROADMAP.md` refused screen-space global illumination because its error is unbounded — a ray
141
+ * that leaves the frame has no answer, and every technique that ships one anyway invents one from
142
+ * whatever happened to be on screen. This does not reverse that refusal: the screen is an
143
+ * accelerator with a world-space distance field behind it, whose error is bounded by its own
144
+ * resolution, and a probe volume behind *that*, which is never wrong and only ever coarse.
145
+ *
146
+ * **CPU-side, and reached by nobody who does not ask.** There is no WGSL, no pass and no renderer
147
+ * option yet, so no scene renders differently for these existing — this wave's first constraint,
148
+ * measured rather than asserted: the eight published scenes are 0 of 921,600 pixels against the
149
+ * build before them, and `core-only` is byte-identical with these exported and without them.
150
+ * Exported anyway, for the reason `bake/cluster.ts` was not: a capability with no route out of its
151
+ * package is one the first consumer finds by failing a boundary test.
152
+ */
153
+ export { GI_SOURCE_FIELD, GI_SOURCE_PROBES, GI_SOURCE_SCREEN, newIndirectResult, traceIndirect, } from './render/gi/chain.js';
154
+ export { GLOBAL_FIELD_BLEND, composeGlobalField, createGlobalField, sampleGlobalField, } from './render/gi/globalField.js';
155
+ export { GI_SCREEN_MARCH, SCREEN_TRACE_BIAS_M, screenRayOrigin, traceScreen, } from './render/gi/traceScreen.js';
156
+ export { FIELD_MARCH, coneRadiusAt, newFieldHit, traceField } from './render/gi/traceField.js';
157
+ export { PROBE_VISIBILITY_SHARPNESS, bakeProbeVisibility, createProbeVisibility, probeUpdateSchedule, probeVisibilityWeight, sampleProbeVolume, visibleProbes, } from './render/gi/probeVolume.js';
158
+ export { DENOISE_MIN_ALPHA, filterStepFor, newTemporalPixel, spatialDenoise, temporalDenoise, } from './render/gi/denoise.js';
159
+ export { reflectDirection, reflectionCone, traceReflection } from './render/gi/reflection.js';
160
+ /*
161
+ * The pass that draws the renderer's field on a device. WebGPU only, and it says so at
162
+ * registration rather than at the first frame: a distance field is composed by compute and WebGL2
163
+ * has none. It no longer composes one of its own — `addDistanceField` is how a scene declares
164
+ * what its indirect light may be traced against, and this marches what the renderer made of them.
165
+ */
166
+ export { GiFieldPass } from './render/backend/webgpu/giFieldPass.js';
167
+ export { DistanceFieldScene, MAX_DISTANCE_FIELDS } from './render/gi/fieldScene.js';
168
+ export { placeCascade } from './render/gi/globalField.js';
90
169
  /*
91
170
  * The acceptance probe, exported because a consumer may want to ask the question itself — an
92
171
  * editor deciding whether to offer a backend, a shell deciding what to tell the player — and
@@ -225,6 +304,13 @@ export { DebugLines } from './render/debugLines.js';
225
304
  * over and what a drag does, and fills line buffers the caller draws with `drawLines`.
226
305
  * Nothing here touches a context.
227
306
  */
307
+ /*
308
+ * Ray intersection, exported because a consumer building a tool needs the same arithmetic the
309
+ * gizmo does. `editor/src/viewport/gizmo.ts` reaching into `src/math/intersect.ts` by path would
310
+ * be the editor using a private hook, which `ARCHITECTURE.md` treats as a hole in the engine
311
+ * rather than as the editor's problem — so the hole is closed here.
312
+ */
313
+ export { rayClosestOnLine, rayPlane, raySphere } from './math/intersect.js';
228
314
  export { GIZMO_GROUP_ACTIVE, GIZMO_GROUP_COLORS, GIZMO_GROUP_COUNT, GIZMO_GROUP_NEUTRAL, GIZMO_GROUP_X, GIZMO_GROUP_Y, GIZMO_GROUP_Z, GIZMO_NONE, GIZMO_ROTATE_X, GIZMO_ROTATE_Y, GIZMO_ROTATE_Z, GIZMO_SCALE_UNIFORM, GIZMO_SCALE_X, GIZMO_SCALE_Y, GIZMO_SCALE_Z, GIZMO_TRANSLATE_X, GIZMO_TRANSLATE_XY, GIZMO_TRANSLATE_Y, GIZMO_TRANSLATE_YZ, GIZMO_TRANSLATE_Z, GIZMO_TRANSLATE_ZX, Gizmo, gizmoScaleFor, } from './render/gizmo.js';
229
315
  export { RainField } from './render/rainField.js';
230
316
  export { coveredAbove } from './physics/heightSurface.js';
@@ -286,6 +372,21 @@ export { bakeOverlay, drawPixelText, pixelTextCells, pixelTextSize } from './ui/
286
372
  export { FpsMeter } from './dev/fpsMeter.js';
287
373
  export { CONTACT_LIMIT, createContactReports, describeContacts } from './dev/contactProbe.js';
288
374
  export { GPU_SLOTS, GpuTimer } from './render/gpuTimer.js';
375
+ /*
376
+ * Per-pass timings, which are a readout rather than a frame graph.
377
+ *
378
+ * **`render/frame/index.ts` says nothing under it may be reached from outside `render/` yet, and
379
+ * this module is the exception with a reason.** The rest of that directory is the graph's
380
+ * construction — declarations, lifetimes, aliasing, scheduling — whose public shape the design
381
+ * leaves to a later phase, and exporting it now would fix a shape nobody has had to live with.
382
+ * A timing readout is not that: it is seven functions over three arrays, its own header argues why
383
+ * it is not a widening of `GpuTimer`, and anything that profiles a frame needs it.
384
+ *
385
+ * The editor is what made this concrete. `AGENTS.md`'s rule is that a consumer needing a private
386
+ * import has found a hole in the engine, and the hole is fixed here rather than worked around
387
+ * there — which is the whole reason the editor was built outside `packages/`.
388
+ */
389
+ export { createPassTimings, passLabel, passMs, recordPassLabel, recordPassSample, resetPassTimings, } from './render/frame/passTimings.js';
289
390
  export { classifyRightGesture } from './input/rightGesture.js';
290
391
  export { createWindState, sampleWind } from './render/wind.js';
291
392
  export { advanceWindField, createWindField } from './render/windField.js';
@@ -329,3 +430,12 @@ export { parseSdfFont } from './render/sdfFont.js';
329
430
  export { DEFAULT_SDF_TEXT_STYLE, SdfTextLayout } from './render/sdfTextLayout.js';
330
431
  export { WindStreakRenderer } from './render/windStreakRenderer.js';
331
432
  export { buildTree } from './geometry/treeBuilder.js';
433
+ /*
434
+ * Large worlds: the cell grid that streams and freezes, and the origin that rendering rebases
435
+ * against while the simulation never does. See `world/rebase.ts` for why that asymmetry is the
436
+ * rule the whole design hangs on.
437
+ */
438
+ export { cellBounds, cellCoord, cellCoordInRange, cellCoordsOf, cellIdFor, cellIdFrom, cellsInFrustum, cellsInRadius, createCellGrid, } from './world/cell.js';
439
+ export { renderOrigin, toRenderSpace, toWorldSpace } from './world/rebase.js';
440
+ export { cameraPositionFrom, cellPredictor, createCellStream, pumpCellStream, setCellStreamOrigin, } from './world/cellStream.js';
441
+ export { createFrozenCells, freezeCell, frozenEntities, frozenFingerprintContribution, isCellFrozen, thawCell, } from './world/freeze.js';