@forgeax/engine-render 0.1.33 → 0.1.34

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 (518) hide show
  1. package/README.md +1010 -124
  2. package/dist/assembly/backend-contract.d.ts +2 -0
  3. package/dist/assembly/backend-contract.d.ts.map +1 -1
  4. package/dist/assembly/barrel-distortion-inspection.d.ts +3 -0
  5. package/dist/assembly/barrel-distortion-inspection.d.ts.map +1 -0
  6. package/dist/assembly/canvas-draw-guard.d.ts +3 -0
  7. package/dist/assembly/canvas-draw-guard.d.ts.map +1 -0
  8. package/dist/assembly/cloud-shadow-entry.d.ts +4 -0
  9. package/dist/assembly/cloud-shadow-entry.d.ts.map +1 -0
  10. package/dist/assembly/dynamic-geometry-host.d.ts +4 -1
  11. package/dist/assembly/dynamic-geometry-host.d.ts.map +1 -1
  12. package/dist/assembly/dynamic-geometry-runtime.d.ts +1 -1
  13. package/dist/assembly/dynamic-geometry-runtime.d.ts.map +1 -1
  14. package/dist/assembly/factory.d.ts +1 -1
  15. package/dist/assembly/factory.d.ts.map +1 -1
  16. package/dist/assembly/host-contract.d.ts +3 -3
  17. package/dist/assembly/host-contract.d.ts.map +1 -1
  18. package/dist/assembly/material/assembly.d.ts.map +1 -1
  19. package/dist/assembly/material/pipeline-helpers.d.ts +5 -0
  20. package/dist/assembly/material/pipeline-helpers.d.ts.map +1 -1
  21. package/dist/assembly/material-shader-policy.d.ts +16 -2
  22. package/dist/assembly/material-shader-policy.d.ts.map +1 -1
  23. package/dist/assembly/material-shader-prewarm.d.ts.map +1 -1
  24. package/dist/assembly/recovery/device-loss-fanout.d.ts.map +1 -1
  25. package/dist/assembly/recovery/renderer-recover.d.ts +2 -0
  26. package/dist/assembly/recovery/renderer-recover.d.ts.map +1 -1
  27. package/dist/assembly/render-feature-post-processes.d.ts +6 -0
  28. package/dist/assembly/render-feature-post-processes.d.ts.map +1 -0
  29. package/dist/assembly/renderer-facade.d.ts.map +1 -1
  30. package/dist/assembly/renderer-feature-inspection.d.ts +1 -1
  31. package/dist/assembly/renderer-feature-inspection.d.ts.map +1 -1
  32. package/dist/assembly/renderer-frame-transaction.d.ts +12 -0
  33. package/dist/assembly/renderer-frame-transaction.d.ts.map +1 -1
  34. package/dist/assembly/renderer-inspection.d.ts +11 -0
  35. package/dist/assembly/renderer-inspection.d.ts.map +1 -1
  36. package/dist/assembly/webgpu-pbr-ready.d.ts +1 -0
  37. package/dist/assembly/webgpu-pbr-ready.d.ts.map +1 -1
  38. package/dist/assembly/webgpu-ready-per-pass.d.ts +39 -0
  39. package/dist/assembly/webgpu-ready-per-pass.d.ts.map +1 -0
  40. package/dist/assembly/webgpu-ready.d.ts +1 -1
  41. package/dist/assembly/webgpu-ready.d.ts.map +1 -1
  42. package/dist/assembly/webgpu-renderer-bootstrap.d.ts.map +1 -1
  43. package/dist/assembly/webgpu-renderer-observation-owner.d.ts +35 -0
  44. package/dist/assembly/webgpu-renderer-observation-owner.d.ts.map +1 -0
  45. package/dist/assembly/webgpu-renderer.d.ts +1 -1
  46. package/dist/assembly/webgpu-renderer.d.ts.map +1 -1
  47. package/dist/authoring.mjs +1 -1
  48. package/dist/barrel-distortion.d.ts +47 -0
  49. package/dist/barrel-distortion.d.ts.map +1 -0
  50. package/dist/{chunk-PA5DRJNI.mjs → chunk-ELJFJ2BU.mjs} +2 -2
  51. package/dist/chunk-ELJFJ2BU.mjs.map +1 -0
  52. package/dist/{chunk-ZDQWGQW7.mjs → chunk-HEB564WF.mjs} +4 -4
  53. package/dist/{chunk-ZDQWGQW7.mjs.map → chunk-HEB564WF.mjs.map} +1 -1
  54. package/dist/{chunk-VOZWDM3L.mjs → chunk-MKDIT2EM.mjs} +1307 -188
  55. package/dist/chunk-MKDIT2EM.mjs.map +1 -0
  56. package/dist/{chunk-LEAXOMYJ.mjs → chunk-OCDLEI6I.mjs} +3 -3
  57. package/dist/{chunk-LEAXOMYJ.mjs.map → chunk-OCDLEI6I.mjs.map} +1 -1
  58. package/dist/chunk-ODZWR6JQ.mjs +4927 -0
  59. package/dist/chunk-ODZWR6JQ.mjs.map +1 -0
  60. package/dist/{chunk-3RTCJTH4.mjs → chunk-SHFUFJ5N.mjs} +184 -13
  61. package/dist/chunk-SHFUFJ5N.mjs.map +1 -0
  62. package/dist/{chunk-OO3DRYK7.mjs → chunk-UI5SFODX.mjs} +176 -96
  63. package/dist/chunk-UI5SFODX.mjs.map +1 -0
  64. package/dist/{chunk-PLAXOOX5.mjs → chunk-UINHLCZA.mjs} +561 -79
  65. package/dist/chunk-UINHLCZA.mjs.map +1 -0
  66. package/dist/{chunk-NB2S7ZRX.mjs → chunk-WEX45HKH.mjs} +127 -386
  67. package/dist/chunk-WEX45HKH.mjs.map +1 -0
  68. package/dist/cloud/density.d.ts +55 -0
  69. package/dist/cloud/density.d.ts.map +1 -0
  70. package/dist/cloud/extract.d.ts +25 -0
  71. package/dist/cloud/extract.d.ts.map +1 -0
  72. package/dist/cloud/feature.d.ts +57 -0
  73. package/dist/cloud/feature.d.ts.map +1 -0
  74. package/dist/cloud/frame-context.d.ts +8 -0
  75. package/dist/cloud/frame-context.d.ts.map +1 -0
  76. package/dist/cloud/inspection.d.ts +59 -0
  77. package/dist/cloud/inspection.d.ts.map +1 -0
  78. package/dist/cloud/optics.d.ts +56 -0
  79. package/dist/cloud/optics.d.ts.map +1 -0
  80. package/dist/cloud/parameters.d.ts +73 -0
  81. package/dist/cloud/parameters.d.ts.map +1 -0
  82. package/dist/cloud/resources.d.ts +60 -0
  83. package/dist/cloud/resources.d.ts.map +1 -0
  84. package/dist/cloud/shadow.d.ts +42 -0
  85. package/dist/cloud/shadow.d.ts.map +1 -0
  86. package/dist/cloud/temporal.d.ts +74 -0
  87. package/dist/cloud/temporal.d.ts.map +1 -0
  88. package/dist/components/barrel-distortion.d.ts +13 -0
  89. package/dist/components/barrel-distortion.d.ts.map +1 -0
  90. package/dist/components/cloud-layer.d.ts +29 -0
  91. package/dist/components/cloud-layer.d.ts.map +1 -0
  92. package/dist/components/index.d.ts +2 -0
  93. package/dist/components/index.d.ts.map +1 -1
  94. package/dist/components/instances.d.ts +20 -6
  95. package/dist/components/instances.d.ts.map +1 -1
  96. package/dist/construct-renderer.d.ts +1 -1
  97. package/dist/construct-renderer.d.ts.map +1 -1
  98. package/dist/construct-renderer.mjs +6759 -2875
  99. package/dist/construct-renderer.mjs.map +1 -1
  100. package/dist/dynamic-geometry.d.ts +5 -1
  101. package/dist/dynamic-geometry.d.ts.map +1 -1
  102. package/dist/errors/cloud.d.ts +59 -0
  103. package/dist/errors/cloud.d.ts.map +1 -0
  104. package/dist/errors/render.d.ts +18 -4
  105. package/dist/errors/render.d.ts.map +1 -1
  106. package/dist/examples/single-layer-medium-surface.d.ts +47 -0
  107. package/dist/examples/single-layer-medium-surface.d.ts.map +1 -0
  108. package/dist/extract/camera.d.ts.map +1 -1
  109. package/dist/extract/contracts.d.ts +5 -1
  110. package/dist/extract/contracts.d.ts.map +1 -1
  111. package/dist/features/barrel-distortion.d.ts +29 -0
  112. package/dist/features/barrel-distortion.d.ts.map +1 -0
  113. package/dist/features/error-reporting.d.ts +6 -2
  114. package/dist/features/error-reporting.d.ts.map +1 -1
  115. package/dist/features/host.d.ts +10 -0
  116. package/dist/features/host.d.ts.map +1 -1
  117. package/dist/features/plan.d.ts +8 -0
  118. package/dist/features/plan.d.ts.map +1 -1
  119. package/dist/features/render-graph-contribution.d.ts +5 -0
  120. package/dist/features/render-graph-contribution.d.ts.map +1 -1
  121. package/dist/features/render-graph-raster.d.ts +1 -0
  122. package/dist/features/render-graph-raster.d.ts.map +1 -1
  123. package/dist/features/targets.d.ts +2 -0
  124. package/dist/features/targets.d.ts.map +1 -1
  125. package/dist/features/types.d.ts +44 -0
  126. package/dist/features/types.d.ts.map +1 -1
  127. package/dist/fullscreen-post-process-pass.d.ts +15 -7
  128. package/dist/fullscreen-post-process-pass.d.ts.map +1 -1
  129. package/dist/gpu-dirty-ranges.d.ts +11 -0
  130. package/dist/gpu-dirty-ranges.d.ts.map +1 -0
  131. package/dist/gpu-driven/batch-topology.d.ts +17 -0
  132. package/dist/gpu-driven/batch-topology.d.ts.map +1 -1
  133. package/dist/gpu-driven/material-artifacts.d.ts +6 -0
  134. package/dist/gpu-driven/material-artifacts.d.ts.map +1 -1
  135. package/dist/gpu-driven/prepared-draw.d.ts.map +1 -1
  136. package/dist/gpu-driven/production-raster-material.d.ts +178 -0
  137. package/dist/gpu-driven/production-raster-material.d.ts.map +1 -0
  138. package/dist/gpu-driven/production-raster.d.ts +47 -3
  139. package/dist/gpu-driven/production-raster.d.ts.map +1 -1
  140. package/dist/gpu-driven/resource-allocation.d.ts +31 -0
  141. package/dist/gpu-driven/resource-allocation.d.ts.map +1 -0
  142. package/dist/gpu-driven/shadow-ownership.d.ts +14 -0
  143. package/dist/gpu-driven/shadow-ownership.d.ts.map +1 -0
  144. package/dist/gpu-driven/view-gpu.d.ts +26 -2
  145. package/dist/gpu-driven/view-gpu.d.ts.map +1 -1
  146. package/dist/gpu-scene.d.ts +5 -0
  147. package/dist/gpu-scene.d.ts.map +1 -1
  148. package/dist/hdrp-buffers.d.ts +2 -2
  149. package/dist/hdrp-buffers.d.ts.map +1 -1
  150. package/dist/ibl/skylight-bind-group.d.ts +10 -1
  151. package/dist/ibl/skylight-bind-group.d.ts.map +1 -1
  152. package/dist/index.d.ts +27 -7
  153. package/dist/index.d.ts.map +1 -1
  154. package/dist/index.mjs +258 -8
  155. package/dist/index.mjs.map +1 -1
  156. package/dist/inspection-types.d.ts +135 -1
  157. package/dist/inspection-types.d.ts.map +1 -1
  158. package/dist/instance-buffer-cache.d.ts +4 -4
  159. package/dist/instance-buffer-cache.d.ts.map +1 -1
  160. package/dist/instances.d.ts +48 -100
  161. package/dist/instances.d.ts.map +1 -1
  162. package/dist/internal.mjs +7 -7
  163. package/dist/pbr-pipeline.d.ts +19 -1
  164. package/dist/pbr-pipeline.d.ts.map +1 -1
  165. package/dist/pipeline/single-layer-medium-passes.d.ts +73 -0
  166. package/dist/pipeline/single-layer-medium-passes.d.ts.map +1 -0
  167. package/dist/pipeline/standard-forward-lane.d.ts.map +1 -1
  168. package/dist/pipeline/standard-output/graph.d.ts.map +1 -1
  169. package/dist/pipeline/standard-output/types.d.ts +4 -2
  170. package/dist/pipeline/standard-output/types.d.ts.map +1 -1
  171. package/dist/pipeline/standard-pipeline.d.ts.map +1 -1
  172. package/dist/pipeline/standard-post.d.ts.map +1 -1
  173. package/dist/pipeline-builder.d.ts +1 -1
  174. package/dist/pipeline-builder.d.ts.map +1 -1
  175. package/dist/plugin.d.ts.map +1 -1
  176. package/dist/prepare/prepared-graphics-resolver.d.ts +2 -0
  177. package/dist/prepare/prepared-graphics-resolver.d.ts.map +1 -1
  178. package/dist/record/barrel-distortion-frame.d.ts +15 -0
  179. package/dist/record/barrel-distortion-frame.d.ts.map +1 -0
  180. package/dist/record/dynamic-geometry-consumption.d.ts +10 -2
  181. package/dist/record/dynamic-geometry-consumption.d.ts.map +1 -1
  182. package/dist/record/fold-buckets.d.ts +95 -0
  183. package/dist/record/fold-buckets.d.ts.map +1 -0
  184. package/dist/record/fold-instance-buffer.d.ts +6 -0
  185. package/dist/record/fold-instance-buffer.d.ts.map +1 -0
  186. package/dist/record/frame-lighting.d.ts +1 -0
  187. package/dist/record/frame-lighting.d.ts.map +1 -1
  188. package/dist/record/frame-snapshot.d.ts +37 -3
  189. package/dist/record/frame-snapshot.d.ts.map +1 -1
  190. package/dist/record/frame.d.ts +10 -61
  191. package/dist/record/frame.d.ts.map +1 -1
  192. package/dist/record/gpu-draw-receipts.d.ts +11 -0
  193. package/dist/record/gpu-draw-receipts.d.ts.map +1 -0
  194. package/dist/record/gpu-timing.d.ts +8 -0
  195. package/dist/record/gpu-timing.d.ts.map +1 -1
  196. package/dist/record/main-pass-geometry.d.ts +21 -5
  197. package/dist/record/main-pass-geometry.d.ts.map +1 -1
  198. package/dist/record/main-pass-material.d.ts +7 -1
  199. package/dist/record/main-pass-material.d.ts.map +1 -1
  200. package/dist/record/main-pass-sprite-draws.d.ts.map +1 -1
  201. package/dist/record/main-pass.d.ts +13 -2
  202. package/dist/record/main-pass.d.ts.map +1 -1
  203. package/dist/record/motion-blur-receipt.d.ts +11 -0
  204. package/dist/record/motion-blur-receipt.d.ts.map +1 -0
  205. package/dist/record/probe-blend-buffer.d.ts +16 -0
  206. package/dist/record/probe-blend-buffer.d.ts.map +1 -0
  207. package/dist/record/recovery-pipeline.d.ts.map +1 -1
  208. package/dist/record/render-context.d.ts +22 -3
  209. package/dist/record/render-context.d.ts.map +1 -1
  210. package/dist/record/shadow-pass.d.ts +2 -0
  211. package/dist/record/shadow-pass.d.ts.map +1 -1
  212. package/dist/record/skybox-post-pass.d.ts +1 -1
  213. package/dist/record/skybox-post-pass.d.ts.map +1 -1
  214. package/dist/record/typed-frame-graph.d.ts +22 -2
  215. package/dist/record/typed-frame-graph.d.ts.map +1 -1
  216. package/dist/record/view-ubo.d.ts +11 -3
  217. package/dist/record/view-ubo.d.ts.map +1 -1
  218. package/dist/record/volume-params.d.ts +9 -0
  219. package/dist/record/volume-params.d.ts.map +1 -0
  220. package/dist/recovery/render-system-candidate.d.ts +10 -0
  221. package/dist/recovery/render-system-candidate.d.ts.map +1 -1
  222. package/dist/reflection/record-owner.d.ts.map +1 -1
  223. package/dist/render-contract.d.ts +42 -4
  224. package/dist/render-contract.d.ts.map +1 -1
  225. package/dist/render-graph-primitives.d.ts.map +1 -1
  226. package/dist/render-pipeline.d.ts +103 -2
  227. package/dist/render-pipeline.d.ts.map +1 -1
  228. package/dist/render-system-extract-tail.d.ts +3 -2
  229. package/dist/render-system-extract-tail.d.ts.map +1 -1
  230. package/dist/render-system-extract.d.ts +16 -9
  231. package/dist/render-system-extract.d.ts.map +1 -1
  232. package/dist/render-system-presentation.d.ts +23 -0
  233. package/dist/render-system-presentation.d.ts.map +1 -0
  234. package/dist/render-system-producer-roots.d.ts +10 -0
  235. package/dist/render-system-producer-roots.d.ts.map +1 -0
  236. package/dist/render-system-residency.d.ts +12 -0
  237. package/dist/render-system-residency.d.ts.map +1 -0
  238. package/dist/render-system.d.ts +19 -14
  239. package/dist/render-system.d.ts.map +1 -1
  240. package/dist/scene/probe-blend.d.ts +27 -0
  241. package/dist/scene/probe-blend.d.ts.map +1 -1
  242. package/dist/scene/render-scene.d.ts +12 -5
  243. package/dist/scene/render-scene.d.ts.map +1 -1
  244. package/dist/ssao-buffers.d.ts +9 -0
  245. package/dist/ssao-buffers.d.ts.map +1 -1
  246. package/dist/surface/dynamic-input.d.ts +133 -0
  247. package/dist/surface/dynamic-input.d.ts.map +1 -0
  248. package/dist/surface/gpu-driven.d.ts +41 -0
  249. package/dist/surface/gpu-driven.d.ts.map +1 -0
  250. package/dist/surface/submission-observation.d.ts +136 -0
  251. package/dist/surface/submission-observation.d.ts.map +1 -0
  252. package/dist/systems/skin-palette-allocator.d.ts.map +1 -1
  253. package/dist/temporal/gpu.d.ts +13 -1
  254. package/dist/temporal/gpu.d.ts.map +1 -1
  255. package/dist/temporal/index.mjs +5 -5
  256. package/dist/temporal/inspection.d.ts.map +1 -1
  257. package/dist/temporal/standard-scene-data.d.ts.map +1 -1
  258. package/dist/temporal/target-coverage-attachment.d.ts.map +1 -1
  259. package/dist/transmission/single-layer-medium.d.ts +89 -0
  260. package/dist/transmission/single-layer-medium.d.ts.map +1 -0
  261. package/dist/typed-render-graph-primitives.d.ts +14 -2
  262. package/dist/typed-render-graph-primitives.d.ts.map +1 -1
  263. package/dist/volume/component.d.ts +9 -4
  264. package/dist/volume/component.d.ts.map +1 -1
  265. package/dist/volume/extract.d.ts +1 -1
  266. package/dist/volume/extract.d.ts.map +1 -1
  267. package/dist/volume/inspection.d.ts +2 -0
  268. package/dist/volume/inspection.d.ts.map +1 -1
  269. package/dist/volume/passes.d.ts +4 -1
  270. package/dist/volume/passes.d.ts.map +1 -1
  271. package/dist/volume/resources.d.ts.map +1 -1
  272. package/package.json +22 -21
  273. package/src/__tests__/atmosphere-daylight.dawn.test.ts +72 -0
  274. package/src/__tests__/barrel-distortion-lkg.integration.test.ts +353 -0
  275. package/src/__tests__/barrel-distortion.dawn.test.ts +41 -0
  276. package/src/__tests__/barrel-distortion.unit.test.ts +364 -0
  277. package/src/__tests__/construct-renderer-identity.unit.test.ts +49 -0
  278. package/src/__tests__/detached-recovery-graph.unit.test.ts +277 -0
  279. package/src/__tests__/device-loss-fanout.unit.test.ts +36 -1
  280. package/src/__tests__/docs-gate-green.unit.test.ts +112 -1
  281. package/src/__tests__/factory-contract.integration.test.ts +295 -45
  282. package/src/__tests__/feature-depth-input.dawn.test.ts +129 -2
  283. package/src/__tests__/fullscreen-feature-plan.unit.test.ts +14 -3
  284. package/src/__tests__/gpu-dirty-ranges.unit.test.ts +22 -0
  285. package/src/__tests__/gpu-driven-baseline.characterization.test.ts +16 -3
  286. package/src/__tests__/gpu-driven-batch-topology-pbr.unit.test.ts +12 -0
  287. package/src/__tests__/gpu-driven-material-artifacts.unit.test.ts +143 -0
  288. package/src/__tests__/gpu-driven-pbr.dawn.test.ts +2 -2
  289. package/src/__tests__/gpu-driven-production.integration.test.ts +482 -3
  290. package/src/__tests__/gpu-driven-shadow-views.dawn.test.ts +6 -5
  291. package/src/__tests__/gpu-driven-view-gpu-evidence.ts +94 -6
  292. package/src/__tests__/gpu-driven-view-graph.integration.test.ts +18 -2
  293. package/src/__tests__/gpu-driven-view.browser.test.ts +5 -8
  294. package/src/__tests__/gpu-driven-view.dawn.test.ts +6 -2
  295. package/src/__tests__/gpu-driven-view.unit.test.ts +1 -1
  296. package/src/__tests__/gpu-pass-timing-lifecycle.integration.test.ts +1 -0
  297. package/src/__tests__/gpu-pass-timing.browser.test.ts +6 -4
  298. package/src/__tests__/gpu-pass-timing.rhi-null.unit.test.ts +1 -0
  299. package/src/__tests__/gpu-scene.unit.test.ts +119 -9
  300. package/src/__tests__/instance-allocation-failure.unit.test.ts +57 -0
  301. package/src/__tests__/instances-culling.integration.test.ts +10 -36
  302. package/src/__tests__/instances-store.unit.test.ts +97 -305
  303. package/src/__tests__/instances-world-ownership.integration.test.ts +62 -0
  304. package/src/__tests__/main-pass-material-medium-selection.unit.test.ts +146 -0
  305. package/src/__tests__/material-shader-prewarm.unit.test.ts +59 -0
  306. package/src/__tests__/material-snapshot-mutation.integration.test.ts +39 -1
  307. package/src/__tests__/morph-culling-reentry.dawn.test.ts +5 -5
  308. package/src/__tests__/morph-visual-evidence.integration.test.ts +8 -7
  309. package/src/__tests__/motion-blur-compute.dawn.test.ts +13 -7
  310. package/src/__tests__/motion-blur.pixel.integration.test.ts +2 -2
  311. package/src/__tests__/nineslice-handle-surface.unit.test.ts +4 -4
  312. package/src/__tests__/pbr-view-layout.unit.test.ts +9 -0
  313. package/src/__tests__/physical-clearcoat.integration.test.ts +58 -0
  314. package/src/__tests__/prepared-graphics-capability.unit.test.ts +66 -2
  315. package/src/__tests__/probe-bindings.unit.test.ts +582 -1
  316. package/src/__tests__/probe-scene-dirty.integration.test.ts +28 -1
  317. package/src/__tests__/receipt-observation-retirement.unit.test.ts +65 -0
  318. package/src/__tests__/recovery-candidate-prepare.contract.test.ts +69 -0
  319. package/src/__tests__/render-error-code-owner.test-d.ts +7 -0
  320. package/src/__tests__/render-feature-diagnostics.unit.test.ts +15 -2
  321. package/src/__tests__/render-feature-error-reporting.unit.test.ts +21 -1
  322. package/src/__tests__/render-feature-prepared-diagnostics.unit.test.ts +10 -1
  323. package/src/__tests__/render-feature-retry.unit.test.ts +12 -1
  324. package/src/__tests__/render-scene-mixed-updates.integration.test.ts +145 -19
  325. package/src/__tests__/render-scene-projection.unit.test.ts +36 -11
  326. package/src/__tests__/render-scene-temporal-retry.unit.test.ts +20 -20
  327. package/src/__tests__/render-target-probe-soak.perf.test.ts +2 -2
  328. package/src/__tests__/render-temporal-environment-consumer.browser.test.ts +3 -3
  329. package/src/__tests__/render-temporal-environment-consumer.dawn.test.ts +5 -2
  330. package/src/__tests__/renderer-factory-material-contract.unit.test.ts +41 -7
  331. package/src/__tests__/renderer-frame-generation-fence.integration.test.ts +16 -0
  332. package/src/__tests__/renderer-frame-transaction.integration.test.ts +26 -10
  333. package/src/__tests__/resource-allocation.unit.test.ts +123 -0
  334. package/src/__tests__/single-layer-medium-passes.unit.test.ts +275 -0
  335. package/src/__tests__/skin-motion-regression.unit.test.ts +70 -1
  336. package/src/__tests__/skin-palette-recycling.dawn.test.ts +97 -0
  337. package/src/__tests__/skinned-shadow-caster.test.ts +15 -0
  338. package/src/__tests__/ssr-carrier-public-contract.unit.test.ts +21 -0
  339. package/src/__tests__/ssr-gpu-dispatch.browser.test.ts +4 -4
  340. package/src/__tests__/ssr-gpu-dispatch.dawn.test.ts +2 -2
  341. package/src/__tests__/ssr-gpu-dispatch.ts +1 -1
  342. package/src/__tests__/ssr-receiver-coverage.dawn.test.ts +6 -2
  343. package/src/__tests__/standard-output-chain-contract.unit.test.ts +28 -0
  344. package/src/__tests__/standard-pbr-artifact-assembly.unit.test.ts +46 -1
  345. package/src/__tests__/standard-pipeline.integration.test.ts +182 -1
  346. package/src/__tests__/surface-dynamic-buffer-lifecycle.unit.test.ts +506 -0
  347. package/src/__tests__/surface-submission-observation.unit.test.ts +812 -0
  348. package/src/__tests__/temporal-history-lifecycle.unit.test.ts +32 -0
  349. package/src/__tests__/temporal-performance.integration.test.ts +3 -3
  350. package/src/__tests__/visibility-instances.integration.test.ts +7 -19
  351. package/src/__tests__/visibility-producer-matrix.integration.test.ts +9 -20
  352. package/src/__tests__/volume-optics-oracle.unit.test.ts +4 -2
  353. package/src/__tests__/volumetric-fog-budget-recovery.integration.test.ts +30 -20
  354. package/src/__tests__/volumetric-fog-errors.unit.test.ts +4 -3
  355. package/src/__tests__/volumetric-fog-multiple.unit.test.ts +55 -0
  356. package/src/__tests__/volumetric-fog-spot-frame-optics.integration.test.ts +1 -1
  357. package/src/__tests__/volumetric-fog-stage-readback.dawn.test.ts +113 -9
  358. package/src/__tests__/volumetric-fog-topology.unit.test.ts +5 -3
  359. package/src/assembly/backend-contract.ts +30 -0
  360. package/src/assembly/barrel-distortion-inspection.ts +22 -0
  361. package/src/assembly/canvas-draw-guard.ts +11 -0
  362. package/src/assembly/cloud-shadow-entry.ts +22 -0
  363. package/src/assembly/dynamic-geometry-host.ts +65 -37
  364. package/src/assembly/dynamic-geometry-runtime.ts +7 -1
  365. package/src/assembly/factory.ts +2 -30
  366. package/src/assembly/host-contract.ts +3 -3
  367. package/src/assembly/material/assembly.ts +25 -13
  368. package/src/assembly/material/pipeline-helpers.ts +53 -12
  369. package/src/assembly/material-shader-policy.ts +55 -0
  370. package/src/assembly/material-shader-prewarm.ts +21 -10
  371. package/src/assembly/recovery/device-loss-fanout.ts +13 -0
  372. package/src/assembly/recovery/renderer-recover.ts +41 -18
  373. package/src/assembly/render-feature-post-processes.ts +54 -0
  374. package/src/assembly/renderer-facade.ts +4 -1
  375. package/src/assembly/renderer-feature-inspection.ts +8 -2
  376. package/src/assembly/renderer-frame-transaction.ts +23 -0
  377. package/src/assembly/renderer-inspection.ts +28 -0
  378. package/src/assembly/skin-palette-owner.ts +1 -1
  379. package/src/assembly/temporal-post-process-prewarm.ts +3 -3
  380. package/src/assembly/webgpu-pbr-ready.ts +9 -1
  381. package/src/assembly/webgpu-ready-per-pass.ts +104 -0
  382. package/src/assembly/webgpu-ready.ts +115 -61
  383. package/src/assembly/webgpu-renderer-bootstrap.ts +0 -12
  384. package/src/assembly/webgpu-renderer-observation-owner.ts +222 -0
  385. package/src/assembly/webgpu-renderer.ts +214 -252
  386. package/src/assembly/webgpu-vertex-layouts.ts +1 -1
  387. package/src/barrel-distortion.ts +247 -0
  388. package/src/cloud/__tests__/cloud-density-parity.dawn.test.ts +173 -0
  389. package/src/cloud/__tests__/cloud-layer-framebuffer-y.dawn.test.ts +265 -0
  390. package/src/cloud/__tests__/cloud-layer.browser.test.ts +432 -0
  391. package/src/cloud/__tests__/cloud-layer.unit.test.ts +634 -0
  392. package/src/cloud/density.ts +600 -0
  393. package/src/cloud/extract.ts +110 -0
  394. package/src/cloud/feature.ts +1810 -0
  395. package/src/cloud/frame-context.ts +42 -0
  396. package/src/cloud/inspection.ts +130 -0
  397. package/src/cloud/optics.ts +285 -0
  398. package/src/cloud/parameters.ts +255 -0
  399. package/src/cloud/resources.ts +122 -0
  400. package/src/cloud/shadow.ts +167 -0
  401. package/src/cloud/temporal.ts +226 -0
  402. package/src/components/barrel-distortion.ts +46 -0
  403. package/src/components/cloud-layer.ts +47 -0
  404. package/src/components/index.ts +2 -0
  405. package/src/components/instances.ts +18 -45
  406. package/src/components/sprite-instances.ts +1 -1
  407. package/src/construct-renderer.ts +1 -1
  408. package/src/dynamic-geometry.ts +12 -1
  409. package/src/errors/cloud.ts +103 -0
  410. package/src/errors/render.ts +44 -7
  411. package/src/examples/single-layer-medium-surface.ts +59 -0
  412. package/src/extract/camera.ts +15 -1
  413. package/src/extract/contracts.ts +5 -0
  414. package/src/features/barrel-distortion.ts +172 -0
  415. package/src/features/error-reporting.ts +26 -6
  416. package/src/features/host.ts +115 -14
  417. package/src/features/plan.ts +8 -0
  418. package/src/features/render-graph-contribution.ts +9 -0
  419. package/src/features/render-graph-raster.ts +45 -3
  420. package/src/features/targets.ts +66 -0
  421. package/src/features/types.ts +47 -0
  422. package/src/fullscreen-post-process-pass.ts +41 -9
  423. package/src/gpu-dirty-ranges.ts +48 -0
  424. package/src/gpu-driven/batch-topology.ts +53 -0
  425. package/src/gpu-driven/material-artifacts.ts +33 -25
  426. package/src/gpu-driven/prepared-draw.ts +12 -4
  427. package/src/gpu-driven/production-raster-material.ts +1894 -0
  428. package/src/gpu-driven/production-raster.ts +825 -784
  429. package/src/gpu-driven/resource-allocation.ts +142 -0
  430. package/src/gpu-driven/shadow-ownership.ts +71 -0
  431. package/src/gpu-driven/view-gpu.ts +164 -10
  432. package/src/gpu-scene.ts +75 -84
  433. package/src/hdrp-buffers.ts +4 -3
  434. package/src/ibl/skylight-bind-group.ts +49 -7
  435. package/src/index.ts +196 -9
  436. package/src/inspection-types.ts +162 -0
  437. package/src/instance-buffer-cache.ts +4 -4
  438. package/src/instances.ts +149 -569
  439. package/src/pbr-pipeline.ts +219 -13
  440. package/src/pipeline/single-layer-medium-passes.ts +267 -0
  441. package/src/pipeline/standard-forward-lane.ts +313 -57
  442. package/src/pipeline/standard-output/graph.ts +8 -2
  443. package/src/pipeline/standard-output/types.ts +12 -1
  444. package/src/pipeline/standard-pipeline.ts +291 -18
  445. package/src/pipeline/standard-post.ts +90 -24
  446. package/src/pipeline-builder.ts +1 -1
  447. package/src/plugin.ts +4 -0
  448. package/src/points-lines/__tests__/points-lines.bench.ts +17 -8
  449. package/src/prepare/prepared-graphics-resolver.ts +2 -0
  450. package/src/record/__tests__/main-pass-geometry.unit.test.ts +30 -0
  451. package/src/record/__tests__/sprite-material-abi.unit.test.ts +1 -1
  452. package/src/record/__tests__/temporal-payload.unit.test.ts +1 -1
  453. package/src/record/__tests__/transparent-group2-selection.unit.test.ts +11 -0
  454. package/src/record/__tests__/typed-frame-graph-plan.unit.test.ts +1 -0
  455. package/src/record/__tests__/view-ubo-layout.unit.test.ts +4 -3
  456. package/src/record/barrel-distortion-frame.ts +110 -0
  457. package/src/record/dynamic-geometry-consumption.ts +39 -9
  458. package/src/record/fold-buckets.ts +852 -0
  459. package/src/record/fold-instance-buffer.ts +51 -0
  460. package/src/record/frame-lighting.ts +36 -2
  461. package/src/record/frame-snapshot.ts +39 -3
  462. package/src/record/frame.ts +217 -1084
  463. package/src/record/gpu-draw-receipts.ts +38 -0
  464. package/src/record/gpu-timing.ts +48 -5
  465. package/src/record/main-pass-geometry.ts +251 -82
  466. package/src/record/main-pass-material.ts +66 -4
  467. package/src/record/main-pass-sprite-draws.ts +7 -65
  468. package/src/record/main-pass.ts +156 -20
  469. package/src/record/motion-blur-receipt.ts +91 -0
  470. package/src/record/probe-blend-buffer.ts +180 -0
  471. package/src/record/recovery-pipeline.ts +35 -3
  472. package/src/record/render-context.ts +39 -3
  473. package/src/record/shadow-pass.ts +30 -1
  474. package/src/record/skybox-post-pass.ts +28 -4
  475. package/src/record/typed-frame-graph.ts +770 -134
  476. package/src/record/view-ubo.ts +26 -3
  477. package/src/record/volume-params.ts +225 -0
  478. package/src/recovery/render-system-candidate.ts +235 -42
  479. package/src/reflection/record-owner.ts +7 -3
  480. package/src/render-contract.ts +48 -4
  481. package/src/render-graph-primitives.ts +7 -1
  482. package/src/render-pipeline.ts +130 -0
  483. package/src/render-system-extract-tail.ts +57 -61
  484. package/src/render-system-extract.ts +74 -76
  485. package/src/render-system-presentation.ts +56 -0
  486. package/src/render-system-producer-roots.ts +96 -0
  487. package/src/render-system-residency.ts +159 -0
  488. package/src/render-system.ts +347 -363
  489. package/src/scene/probe-blend.ts +96 -4
  490. package/src/scene/render-scene.ts +349 -280
  491. package/src/ssao-buffers.ts +95 -2
  492. package/src/surface/__tests__/dynamic-input.unit.test.ts +374 -0
  493. package/src/surface/__tests__/gpu-driven.unit.test.ts +87 -0
  494. package/src/surface/dynamic-input.ts +658 -0
  495. package/src/surface/gpu-driven.ts +147 -0
  496. package/src/surface/submission-observation.ts +517 -0
  497. package/src/systems/skin-palette-allocator.ts +4 -1
  498. package/src/temporal/gpu.ts +101 -17
  499. package/src/temporal/inspection.ts +7 -1
  500. package/src/temporal/standard-scene-data.ts +5 -0
  501. package/src/temporal/target-coverage-attachment.ts +4 -0
  502. package/src/transmission/__tests__/single-layer-medium.unit.test.ts +149 -0
  503. package/src/transmission/__tests__/standard-transmission.dawn.test.ts +12 -11
  504. package/src/transmission/single-layer-medium.ts +322 -0
  505. package/src/typed-render-graph-primitives.ts +70 -2
  506. package/src/volume/component.ts +10 -9
  507. package/src/volume/extract.ts +11 -5
  508. package/src/volume/inspection.ts +3 -0
  509. package/src/volume/passes.ts +87 -4
  510. package/src/volume/resources.ts +2 -1
  511. package/dist/chunk-3RTCJTH4.mjs.map +0 -1
  512. package/dist/chunk-NB2S7ZRX.mjs.map +0 -1
  513. package/dist/chunk-NNQL7CB3.mjs +0 -1713
  514. package/dist/chunk-NNQL7CB3.mjs.map +0 -1
  515. package/dist/chunk-OO3DRYK7.mjs.map +0 -1
  516. package/dist/chunk-PA5DRJNI.mjs.map +0 -1
  517. package/dist/chunk-PLAXOOX5.mjs.map +0 -1
  518. package/dist/chunk-VOZWDM3L.mjs.map +0 -1
@@ -0,0 +1,600 @@
1
+ import { err, ok, type Result } from '@forgeax/engine-types';
2
+ import { CloudLayerCacheInvalidError, type CloudLayerError } from '../errors/cloud';
3
+ import {
4
+ CLOUD_QUALITY_PROFILES,
5
+ type CloudQualityProfile,
6
+ cloudLayerFormationKey,
7
+ type ValidatedCloudLayer,
8
+ } from './parameters';
9
+
10
+ export interface CloudDensityCache {
11
+ readonly sourceKey: string;
12
+ readonly resolution: number;
13
+ /** R8 normalized density values in z-major, y, x order. */
14
+ readonly data: Uint8Array;
15
+ /**
16
+ * Weather, body and erosion bases in three contiguous R8 planes. `data` is
17
+ * retained as a diagnostic projection for callers that still need the
18
+ * composed density; runtime sampling uses these formation planes so coverage,
19
+ * density and wind remain evaluation controls.
20
+ */
21
+ readonly formationData: Uint8Array;
22
+ readonly formationByteLength: number;
23
+ /** Bytes uploaded for the reusable formation planes. */
24
+ readonly byteLength: number;
25
+ readonly digest: string;
26
+ }
27
+
28
+ export interface CloudDensityCacheSnapshot {
29
+ readonly sourceKey: string;
30
+ readonly resolution: number;
31
+ readonly data: Uint8Array;
32
+ readonly formationData?: Uint8Array;
33
+ readonly digest: string;
34
+ }
35
+
36
+ export interface CloudDensitySample {
37
+ readonly density: number;
38
+ readonly heightFraction: number;
39
+ readonly noise: number;
40
+ readonly weather?: number;
41
+ readonly body?: number;
42
+ readonly erosion?: number;
43
+ }
44
+
45
+ export interface CloudFormationSample {
46
+ readonly weather: number;
47
+ readonly body: number;
48
+ readonly erosion: number;
49
+ readonly noise: number;
50
+ }
51
+
52
+ /**
53
+ * The cache is one world-space period. Keep several lattice cells inside that
54
+ * period so a street view sees separate cloud bodies instead of one broad
55
+ * slab. The lattice wraps at the same boundary as the cache, which prevents
56
+ * the old seam from turning into a repeated horizontal band.
57
+ */
58
+ const CLOUD_SHAPE_CELLS = 4;
59
+ const CLOUD_WEATHER_CELLS = 2;
60
+ const CLOUD_DETAIL_CELLS = CLOUD_SHAPE_CELLS * 3;
61
+ // One advective cache period contains four horizontal and two vertical body cells.
62
+ // Integer periods on every axis make the formation continuous when wind wraps
63
+ // through the cache boundary instead of introducing a seam at y = 0/1.
64
+ export const CLOUD_VERTICAL_CELLS = 1;
65
+ // The cache keeps one advective period, while the formation itself gets two
66
+ // smooth vertical lobes inside that period so the street view does not read as
67
+ // a single sheet. Both are integer-period fields, so the cache seam remains
68
+ // exact.
69
+ const CLOUD_VERTICAL_NOISE_CELLS = 2;
70
+ const CLOUD_DETAIL_VERTICAL_CELLS = 3;
71
+
72
+ function saturate(value: number): number {
73
+ return Math.min(1, Math.max(0, value));
74
+ }
75
+
76
+ function smoothstep(edge0: number, edge1: number, value: number): number {
77
+ const t = saturate((value - edge0) / Math.max(1e-6, edge1 - edge0));
78
+ return t * t * (3 - 2 * t);
79
+ }
80
+
81
+ function fract(value: number): number {
82
+ return value - Math.floor(value);
83
+ }
84
+
85
+ function wrapInteger(value: number, period: number): number {
86
+ const wrapped = value % period;
87
+ return wrapped < 0 ? wrapped + period : wrapped;
88
+ }
89
+
90
+ /** Deterministic integer hash. Every octave uses the same seeded function. */
91
+ function hash3(x: number, y: number, z: number, seed: number): number {
92
+ let value = Math.imul(x | 0, 0x45d9f3b);
93
+ value = Math.imul(value ^ Math.imul(y | 0, 0x119de1f3), 0x45d9f3b);
94
+ value = Math.imul(value ^ Math.imul(z | 0, 0x3449f), 0x45d9f3b);
95
+ value ^= seed | 0;
96
+ value = Math.imul(value ^ (value >>> 16), 0x45d9f3b);
97
+ value = Math.imul(value ^ (value >>> 13), 0x27d4eb2d);
98
+ return ((value ^ (value >>> 16)) >>> 0) / 0x100000000;
99
+ }
100
+
101
+ function valueNoise(
102
+ x: number,
103
+ y: number,
104
+ z: number,
105
+ seed: number,
106
+ periodX = 0,
107
+ periodY = 0,
108
+ periodZ = 0,
109
+ ): number {
110
+ const ix = Math.floor(x);
111
+ const iy = Math.floor(y);
112
+ const iz = Math.floor(z);
113
+ const fx = x - ix;
114
+ const fy = y - iy;
115
+ const fz = z - iz;
116
+ const ux = fx * fx * (3 - 2 * fx);
117
+ const uy = fy * fy * (3 - 2 * fy);
118
+ const uz = fz * fz * (3 - 2 * fz);
119
+ const lattice = (cellX: number, cellY: number, cellZ: number): number =>
120
+ hash3(
121
+ periodX > 0 ? wrapInteger(cellX, periodX) : cellX,
122
+ periodY > 0 ? wrapInteger(cellY, periodY) : cellY,
123
+ periodZ > 0 ? wrapInteger(cellZ, periodZ) : cellZ,
124
+ seed,
125
+ );
126
+ const c000 = lattice(ix, iy, iz);
127
+ const c100 = lattice(ix + 1, iy, iz);
128
+ const c010 = lattice(ix, iy + 1, iz);
129
+ const c110 = lattice(ix + 1, iy + 1, iz);
130
+ const c001 = lattice(ix, iy, iz + 1);
131
+ const c101 = lattice(ix + 1, iy, iz + 1);
132
+ const c011 = lattice(ix, iy + 1, iz + 1);
133
+ const c111 = lattice(ix + 1, iy + 1, iz + 1);
134
+ const x00 = c000 + (c100 - c000) * ux;
135
+ const x10 = c010 + (c110 - c010) * ux;
136
+ const x01 = c001 + (c101 - c001) * ux;
137
+ const x11 = c011 + (c111 - c011) * ux;
138
+ return x00 + (x10 - x00) * uy + (x01 + (x11 - x01) * uy - (x00 + (x10 - x00) * uy)) * uz;
139
+ }
140
+
141
+ /** Bounded Worley-like cellular term used to erode the high frequency field. */
142
+ function cellularNoise(
143
+ x: number,
144
+ y: number,
145
+ z: number,
146
+ seed: number,
147
+ periodXZ = CLOUD_DETAIL_CELLS,
148
+ periodY = 0,
149
+ ): number {
150
+ const ix = Math.floor(x);
151
+ const iy = Math.floor(y);
152
+ const iz = Math.floor(z);
153
+ const fx = x - ix;
154
+ const fy = y - iy;
155
+ const fz = z - iz;
156
+ let nearest = Number.POSITIVE_INFINITY;
157
+ for (let dz = -1; dz <= 1; dz += 1) {
158
+ for (let dy = -1; dy <= 1; dy += 1) {
159
+ for (let dx = -1; dx <= 1; dx += 1) {
160
+ const random = hash3(
161
+ wrapInteger(ix + dx, periodXZ),
162
+ periodY > 0 ? wrapInteger(iy + dy, periodY) : iy + dy,
163
+ wrapInteger(iz + dz, periodXZ),
164
+ seed ^ 0x9e3779b9,
165
+ );
166
+ const px = dx + fract(random * 17.0) - fx;
167
+ const py = dy + fract(random * 31.0) - fy;
168
+ const pz = dz + fract(random * 47.0) - fz;
169
+ nearest = Math.min(nearest, px * px + py * py + pz * pz);
170
+ }
171
+ }
172
+ }
173
+ return 1 - saturate(Math.sqrt(nearest) * 1.25);
174
+ }
175
+
176
+ function heightEnvelope(height: number, body: number, weather: number): number {
177
+ // Let each body choose its own crown height. A fixed upper edge reads as a
178
+ // horizontal slab from the street camera; tying the crown to the cached body
179
+ // makes dense cells grow upward while sparse cells dissolve earlier.
180
+ const lowerEdge = 0.05 + (1 - body) * 0.1;
181
+ const crownEdge = Math.min(0.96, 0.6 + body * 0.3 + weather * 0.08);
182
+ return (
183
+ smoothstep(lowerEdge, Math.min(1, lowerEdge + 0.12), height) *
184
+ (1 - smoothstep(crownEdge, Math.min(1, crownEdge + 0.14), height))
185
+ );
186
+ }
187
+
188
+ /** Coverage shapes the macro body; detail removes material only at its boundary. */
189
+ function composeCloudDensity(
190
+ height: number,
191
+ formation: CloudFormationSample,
192
+ coverage: number,
193
+ ): number {
194
+ const threshold = 1 - coverage * (0.45 + formation.weather * 0.55);
195
+ const covered = smoothstep(0, 0.4, (formation.body - threshold) / Math.max(0.001, 1 - threshold));
196
+ const shaped = covered * heightEnvelope(height, formation.body, formation.weather);
197
+ const erosion = (1 - formation.erosion) * 0.18;
198
+ return saturate((shaped - erosion) / (1 - erosion));
199
+ }
200
+
201
+ function formationField(
202
+ x: number,
203
+ y: number,
204
+ z: number,
205
+ seed: number,
206
+ detailRepeat = 1,
207
+ ): CloudFormationSample {
208
+ // A low-frequency height-aware warp breaks axis-aligned horizontal sheets
209
+ // while keeping the cache periodic on every axis. The warp is evaluated once while
210
+ // the reusable formation cache is built, so runtime ray steps pay no extra
211
+ // noise cost.
212
+ const macroWarpX =
213
+ valueNoise(
214
+ x * CLOUD_WEATHER_CELLS,
215
+ y * CLOUD_VERTICAL_NOISE_CELLS + 3.0,
216
+ z * CLOUD_WEATHER_CELLS,
217
+ seed + 41023,
218
+ CLOUD_WEATHER_CELLS,
219
+ 2,
220
+ CLOUD_WEATHER_CELLS,
221
+ ) - 0.5;
222
+ const macroWarpZ =
223
+ valueNoise(
224
+ x * CLOUD_WEATHER_CELLS + 5.0,
225
+ y * CLOUD_VERTICAL_NOISE_CELLS - 7.0,
226
+ z * CLOUD_WEATHER_CELLS,
227
+ seed + 41023,
228
+ CLOUD_WEATHER_CELLS,
229
+ 2,
230
+ CLOUD_WEATHER_CELLS,
231
+ ) - 0.5;
232
+ const bodyX = x + macroWarpX * 0.3;
233
+ const bodyZ = z + macroWarpZ * 0.3;
234
+ let amplitude = 0.5;
235
+ let frequency = 1;
236
+ let sum = 0;
237
+ let weight = 0;
238
+ for (let octave = 0; octave < 4; octave += 1) {
239
+ const tilePeriod = CLOUD_SHAPE_CELLS * frequency;
240
+ sum +=
241
+ valueNoise(
242
+ bodyX * tilePeriod,
243
+ y * frequency * CLOUD_VERTICAL_NOISE_CELLS,
244
+ bodyZ * tilePeriod,
245
+ seed + octave * 1013,
246
+ tilePeriod,
247
+ frequency * CLOUD_VERTICAL_NOISE_CELLS,
248
+ tilePeriod,
249
+ ) * amplitude;
250
+ weight += amplitude;
251
+ amplitude *= 0.4;
252
+ frequency *= 2;
253
+ }
254
+ // Perlin-Worley-style body remapping: the broad fBm controls the volume and
255
+ // the cellular field only breaks its boundary. Keeping these values
256
+ // separate is what lets a runtime coverage edit preserve the authored cloud
257
+ // bodies instead of regenerating a final alpha volume.
258
+ const broad = sum / weight;
259
+ // Periodic Worley cells provide bounded breakup. The broad field remains
260
+ // dominant so one cache period reads as a few connected formations instead
261
+ // of a repeated grid of small spheres.
262
+ const cells = cellularNoise(
263
+ bodyX * CLOUD_SHAPE_CELLS,
264
+ y * CLOUD_VERTICAL_NOISE_CELLS,
265
+ bodyZ * CLOUD_SHAPE_CELLS,
266
+ seed + 5011,
267
+ CLOUD_SHAPE_CELLS,
268
+ CLOUD_VERTICAL_NOISE_CELLS,
269
+ );
270
+ // Let the broad field carry the silhouette and use cellular noise as a
271
+ // bounded breakup term. A broad-dominant mix keeps the same cache and ray
272
+ // budget while yielding fewer, wider and less spherical cumulus masses.
273
+ const body = saturate(broad * 0.85 + cells * 0.15 + 0.15);
274
+ const weather = smoothstep(
275
+ 0.34,
276
+ 0.66,
277
+ valueNoise(
278
+ x * CLOUD_WEATHER_CELLS,
279
+ 0.37,
280
+ z * CLOUD_WEATHER_CELLS,
281
+ seed + 17041,
282
+ CLOUD_WEATHER_CELLS,
283
+ 1,
284
+ CLOUD_WEATHER_CELLS,
285
+ ),
286
+ );
287
+ const detailX = x * detailRepeat;
288
+ const detailY = y * detailRepeat;
289
+ const detailZ = z * detailRepeat;
290
+ const warpX =
291
+ valueNoise(
292
+ detailX * CLOUD_WEATHER_CELLS,
293
+ detailY * 2 + 11.0,
294
+ detailZ * CLOUD_WEATHER_CELLS,
295
+ seed + 29011,
296
+ CLOUD_WEATHER_CELLS,
297
+ 2,
298
+ CLOUD_WEATHER_CELLS,
299
+ ) - 0.5;
300
+ const warpZ =
301
+ valueNoise(
302
+ detailX * CLOUD_WEATHER_CELLS + 7.0,
303
+ detailY * 2 - 5.0,
304
+ detailZ * CLOUD_WEATHER_CELLS,
305
+ seed + 29011,
306
+ CLOUD_WEATHER_CELLS,
307
+ 2,
308
+ CLOUD_WEATHER_CELLS,
309
+ ) - 0.5;
310
+ const erosion = cellularNoise(
311
+ (detailX + warpX * 0.22) * CLOUD_DETAIL_CELLS,
312
+ detailY * CLOUD_DETAIL_VERTICAL_CELLS,
313
+ (detailZ + warpZ * 0.22) * CLOUD_DETAIL_CELLS,
314
+ seed + 7919,
315
+ CLOUD_DETAIL_CELLS,
316
+ CLOUD_DETAIL_VERTICAL_CELLS,
317
+ );
318
+ const noise = body;
319
+ return { weather, body, erosion, noise };
320
+ }
321
+
322
+ /** Evaluate the reusable weather/body/erosion bases at one world position. */
323
+ export function evaluateCloudFormation(
324
+ params: ValidatedCloudLayer,
325
+ position: ArrayLike<number>,
326
+ timeSeconds = 0,
327
+ ): CloudFormationSample {
328
+ const windX = (params.wind[0] ?? 0) * timeSeconds;
329
+ const windY = (params.wind[1] ?? 0) * timeSeconds;
330
+ const windZ = (params.wind[2] ?? 0) * timeSeconds;
331
+ const relativeHeight =
332
+ ((position[1] ?? 0) - params.baseHeight) / Math.max(1e-6, params.thickness);
333
+ const advectedHeight =
334
+ relativeHeight + (windY * params.scale) / Math.max(1e-6, CLOUD_VERTICAL_CELLS);
335
+ return formationField(
336
+ ((position[0] ?? 0) + windX) * params.scale,
337
+ fract(advectedHeight) * CLOUD_VERTICAL_CELLS,
338
+ ((position[2] ?? 0) + windZ) * params.scale,
339
+ params.seed,
340
+ );
341
+ }
342
+
343
+ /** Analytic field used for cache generation, CPU diagnostics and recovery. */
344
+ export function evaluateCloudDensity(
345
+ params: ValidatedCloudLayer,
346
+ position: ArrayLike<number>,
347
+ timeSeconds = 0,
348
+ ): CloudDensitySample {
349
+ const relativeHeight =
350
+ ((position[1] ?? 0) - params.baseHeight) / Math.max(1e-6, params.thickness);
351
+ if (relativeHeight <= 0 || relativeHeight >= 1 || params.density <= 0 || params.coverage <= 0) {
352
+ return { density: 0, heightFraction: saturate(relativeHeight), noise: 0 };
353
+ }
354
+ const windX = (params.wind[0] ?? 0) * timeSeconds;
355
+ const windY = (params.wind[1] ?? 0) * timeSeconds;
356
+ const windZ = (params.wind[2] ?? 0) * timeSeconds;
357
+ const x = ((position[0] ?? 0) + windX) * params.scale;
358
+ const advectedHeight =
359
+ relativeHeight + (windY * params.scale) / Math.max(1e-6, CLOUD_VERTICAL_CELLS);
360
+ const y = fract(advectedHeight) * CLOUD_VERTICAL_CELLS;
361
+ const z = ((position[2] ?? 0) + windZ) * params.scale;
362
+ const formation = formationField(x, y, z, params.seed, 3);
363
+ const noise = formation.noise;
364
+ return {
365
+ density: composeCloudDensity(relativeHeight, formation, params.coverage) * params.density,
366
+ heightFraction: relativeHeight,
367
+ noise,
368
+ weather: formation.weather,
369
+ body: formation.body,
370
+ erosion: formation.erosion,
371
+ };
372
+ }
373
+
374
+ function cacheIndex(resolution: number, x: number, y: number, z: number): number {
375
+ return z * resolution * resolution + y * resolution + x;
376
+ }
377
+
378
+ function digestBytes(data: Uint8Array): string {
379
+ // FNV-1a is stable and intentionally cheap; this is an identity receipt,
380
+ // not a cryptographic integrity boundary.
381
+ let hash = 0x811c9dc5;
382
+ for (const byte of data) {
383
+ hash ^= byte;
384
+ hash = Math.imul(hash, 0x01000193);
385
+ }
386
+ return (hash >>> 0).toString(16).padStart(8, '0');
387
+ }
388
+
389
+ /** Build one canonical, periodic formation cache. */
390
+ export function buildCloudDensityCache(
391
+ params: ValidatedCloudLayer,
392
+ profile: CloudQualityProfile = CLOUD_QUALITY_PROFILES[params.quality],
393
+ ): CloudDensityCache {
394
+ const resolution = Math.max(4, Math.floor(profile.cacheResolution));
395
+ const data = new Uint8Array(resolution * resolution * resolution);
396
+ const formationData = new Uint8Array(data.byteLength * 3);
397
+ const position = [0, 0, 0];
398
+ for (let z = 0; z < resolution; z += 1) {
399
+ for (let y = 0; y < resolution; y += 1) {
400
+ position[1] = params.baseHeight + ((y + 0.5) / resolution) * params.thickness;
401
+ for (let x = 0; x < resolution; x += 1) {
402
+ // One cache period is one noise period in world space. This makes the
403
+ // x/z addressing periodic and lets advection wrap without seams.
404
+ position[0] = (x + 0.5) / resolution / params.scale;
405
+ position[2] = (z + 0.5) / resolution / params.scale;
406
+ const index = cacheIndex(resolution, x, y, z);
407
+ const formation = evaluateCloudFormation(params, position, 0);
408
+ formationData[index] = Math.round(saturate(formation.weather) * 255);
409
+ formationData[data.byteLength + index] = Math.round(saturate(formation.body) * 255);
410
+ formationData[data.byteLength * 2 + index] = Math.round(saturate(formation.erosion) * 255);
411
+ }
412
+ }
413
+ }
414
+ const cache: CloudDensityCache = {
415
+ sourceKey: cloudLayerFormationKey(params),
416
+ resolution,
417
+ data,
418
+ formationData,
419
+ formationByteLength: formationData.byteLength,
420
+ byteLength: formationData.byteLength,
421
+ digest: digestBytes(formationData),
422
+ };
423
+ for (let z = 0; z < resolution; z++) {
424
+ for (let y = 0; y < resolution; y++) {
425
+ for (let x = 0; x < resolution; x++) {
426
+ position[0] = (x + 0.5) / resolution / params.scale;
427
+ position[1] = params.baseHeight + ((y + 0.5) / resolution) * params.thickness;
428
+ position[2] = (z + 0.5) / resolution / params.scale;
429
+ data[cacheIndex(resolution, x, y, z)] = Math.round(
430
+ saturate(sampleCloudDensity(cache, params, position)) * 255,
431
+ );
432
+ }
433
+ }
434
+ }
435
+ return Object.freeze(cache);
436
+ }
437
+
438
+ /** Export a detached snapshot suitable for persistence or a worker boundary. */
439
+ export function snapshotCloudDensityCache(cache: CloudDensityCache): CloudDensityCacheSnapshot {
440
+ return Object.freeze({
441
+ sourceKey: cache.sourceKey,
442
+ resolution: cache.resolution,
443
+ data: new Uint8Array(cache.data),
444
+ formationData: new Uint8Array(cache.formationData),
445
+ digest: cache.digest,
446
+ });
447
+ }
448
+
449
+ /** Reconstruct a cache without running the procedural generator again. */
450
+ export function reconstructCloudDensityCache(
451
+ params: ValidatedCloudLayer,
452
+ snapshot: CloudDensityCacheSnapshot,
453
+ ): Result<CloudDensityCache, CloudLayerError> {
454
+ const expectedKey = cloudLayerFormationKey(params);
455
+ if (snapshot.sourceKey !== expectedKey) {
456
+ return err(new CloudLayerCacheInvalidError(snapshot.sourceKey, 'source key mismatch'));
457
+ }
458
+ if (
459
+ !Number.isInteger(snapshot.resolution) ||
460
+ snapshot.resolution < 4 ||
461
+ snapshot.resolution > 256 ||
462
+ snapshot.data.byteLength !== snapshot.resolution ** 3 ||
463
+ (snapshot.formationData !== undefined &&
464
+ snapshot.formationData.byteLength !== snapshot.resolution ** 3 * 3)
465
+ ) {
466
+ return err(
467
+ new CloudLayerCacheInvalidError(snapshot.sourceKey, 'resolution and payload length mismatch'),
468
+ );
469
+ }
470
+ const formationData =
471
+ snapshot.formationData === undefined
472
+ ? new Uint8Array(snapshot.data.length * 3).fill(0)
473
+ : new Uint8Array(snapshot.formationData);
474
+ const digest = digestBytes(formationData);
475
+ if (digest !== snapshot.digest) {
476
+ return err(new CloudLayerCacheInvalidError(snapshot.sourceKey, 'payload digest mismatch'));
477
+ }
478
+ const data = new Uint8Array(snapshot.data);
479
+ return ok(
480
+ Object.freeze({
481
+ sourceKey: snapshot.sourceKey,
482
+ resolution: snapshot.resolution,
483
+ data,
484
+ formationData,
485
+ formationByteLength: formationData.byteLength,
486
+ byteLength: formationData.byteLength,
487
+ digest,
488
+ }),
489
+ );
490
+ }
491
+
492
+ function wrapped(value: number, resolution: number): number {
493
+ const result = value - Math.floor(value);
494
+ return Math.min(resolution - 1e-6, Math.max(0, result * resolution));
495
+ }
496
+
497
+ /** Sample the cached field with periodic x/z addressing and wind advection. */
498
+ export function sampleCloudDensity(
499
+ cache: CloudDensityCache,
500
+ params: ValidatedCloudLayer,
501
+ position: ArrayLike<number>,
502
+ timeSeconds = 0,
503
+ ): number {
504
+ const height = ((position[1] ?? 0) - params.baseHeight) / Math.max(1e-6, params.thickness);
505
+ if (height <= 0 || height >= 1 || params.coverage <= 0 || params.density <= 0) return 0;
506
+ const period = 1 / Math.max(1e-6, params.scale);
507
+ const x = wrapped(
508
+ ((position[0] ?? 0) + (params.wind[0] ?? 0) * timeSeconds) / period - 0.5 / cache.resolution,
509
+ cache.resolution,
510
+ );
511
+ const z = wrapped(
512
+ ((position[2] ?? 0) + (params.wind[2] ?? 0) * timeSeconds) / period - 0.5 / cache.resolution,
513
+ cache.resolution,
514
+ );
515
+ // The cache's y axis stores the normalized formation coordinate before
516
+ // vertical advection. Map the authored noise shift back through the same
517
+ // vertical cell aspect used by formationField; the world-space envelope
518
+ // below remains anchored to the current receiver height.
519
+ const advectedHeight =
520
+ height +
521
+ ((params.wind[1] ?? 0) * timeSeconds * params.scale) / Math.max(1e-6, CLOUD_VERTICAL_CELLS);
522
+ const y = wrapped(advectedHeight - 0.5 / cache.resolution, cache.resolution);
523
+ const x0 = Math.floor(x);
524
+ const y0 = Math.floor(y);
525
+ const z0 = Math.floor(z);
526
+ const x1 = (x0 + 1) % cache.resolution;
527
+ const y1 = (y0 + 1) % cache.resolution;
528
+ const z1 = (z0 + 1) % cache.resolution;
529
+ const fx = x - x0;
530
+ const fy = y - y0;
531
+ const fz = z - z0;
532
+ const stride = cache.resolution ** 3;
533
+ if (cache.formationData === undefined || cache.formationData.byteLength < stride * 3) {
534
+ const sampleLegacy = (sx: number, sy: number, sz: number): number =>
535
+ (cache.data[cacheIndex(cache.resolution, sx, sy, sz)] ?? 0) / 255;
536
+ const c00 =
537
+ sampleLegacy(x0, y0, z0) + (sampleLegacy(x1, y0, z0) - sampleLegacy(x0, y0, z0)) * fx;
538
+ const c10 =
539
+ sampleLegacy(x0, y1, z0) + (sampleLegacy(x1, y1, z0) - sampleLegacy(x0, y1, z0)) * fx;
540
+ const c01 =
541
+ sampleLegacy(x0, y0, z1) + (sampleLegacy(x1, y0, z1) - sampleLegacy(x0, y0, z1)) * fx;
542
+ const c11 =
543
+ sampleLegacy(x0, y1, z1) + (sampleLegacy(x1, y1, z1) - sampleLegacy(x0, y1, z1)) * fx;
544
+ return (
545
+ (c00 + (c10 - c00) * fy + (c01 + (c11 - c01) * fy - (c00 + (c10 - c00) * fy)) * fz) *
546
+ heightEnvelope(height, 1, 0.5) *
547
+ params.density
548
+ );
549
+ }
550
+ const samplePlane = (plane: number, sx: number, sy: number, sz: number): number => {
551
+ const payload = cache.formationData;
552
+ const offset = plane * stride + cacheIndex(cache.resolution, sx, sy, sz);
553
+ return (payload[offset] ?? 0) / 255;
554
+ };
555
+ const sample = (plane: number): number => {
556
+ const repeat = plane === 2 ? 3 : 1;
557
+ const sx = wrapped(
558
+ ((x + 0.5) / cache.resolution) * repeat - 0.5 / cache.resolution,
559
+ cache.resolution,
560
+ );
561
+ const sy = wrapped(
562
+ ((y + 0.5) / cache.resolution) * repeat - 0.5 / cache.resolution,
563
+ cache.resolution,
564
+ );
565
+ const sz = wrapped(
566
+ ((z + 0.5) / cache.resolution) * repeat - 0.5 / cache.resolution,
567
+ cache.resolution,
568
+ );
569
+ const x0 = Math.floor(sx),
570
+ y0 = Math.floor(sy),
571
+ z0 = Math.floor(sz);
572
+ const x1 = (x0 + 1) % cache.resolution,
573
+ y1 = (y0 + 1) % cache.resolution,
574
+ z1 = (z0 + 1) % cache.resolution;
575
+ const fx = sx - x0,
576
+ fy = sy - y0,
577
+ fz = sz - z0;
578
+
579
+ const c00 =
580
+ samplePlane(plane, x0, y0, z0) +
581
+ (samplePlane(plane, x1, y0, z0) - samplePlane(plane, x0, y0, z0)) * fx;
582
+ const c10 =
583
+ samplePlane(plane, x0, y1, z0) +
584
+ (samplePlane(plane, x1, y1, z0) - samplePlane(plane, x0, y1, z0)) * fx;
585
+ const c01 =
586
+ samplePlane(plane, x0, y0, z1) +
587
+ (samplePlane(plane, x1, y0, z1) - samplePlane(plane, x0, y0, z1)) * fx;
588
+ const c11 =
589
+ samplePlane(plane, x0, y1, z1) +
590
+ (samplePlane(plane, x1, y1, z1) - samplePlane(plane, x0, y1, z1)) * fx;
591
+ return c00 + (c10 - c00) * fy + (c01 + (c11 - c01) * fy - (c00 + (c10 - c00) * fy)) * fz;
592
+ };
593
+ const weather = sample(0);
594
+ const body = sample(1);
595
+ const erosion = sample(2);
596
+ return (
597
+ composeCloudDensity(height, { weather, body, erosion, noise: body }, params.coverage) *
598
+ params.density
599
+ );
600
+ }
@@ -0,0 +1,110 @@
1
+ import type { EntityHandle, World } from '@forgeax/engine-ecs';
2
+ import { Time } from '@forgeax/engine-ecs';
3
+ import { err, ok, type Result } from '@forgeax/engine-types';
4
+ import { CloudLayer, type CloudLayerData } from '../components/cloud-layer';
5
+ import { DirectionalLight } from '../components/directional-light';
6
+ import { type CloudLayerError, CloudLayerOwnerConflictError } from '../errors/cloud';
7
+ import { cloudLayerSourceKey, type ValidatedCloudLayer, validateCloudLayer } from './parameters';
8
+
9
+ export interface CloudLayerCandidate {
10
+ readonly entityKey: EntityHandle;
11
+ readonly data: CloudLayerData;
12
+ }
13
+
14
+ export interface ExtractedCloudLayer {
15
+ readonly status: 'available';
16
+ readonly entityKey: EntityHandle;
17
+ readonly sourceKey: string;
18
+ readonly params: ValidatedCloudLayer;
19
+ readonly worldTimeSeconds: number;
20
+ /** First World-owned sun selected by the same extraction boundary. */
21
+ readonly sunDirection: readonly [number, number, number] | undefined;
22
+ readonly sunRadiance: readonly [number, number, number] | undefined;
23
+ readonly revision: number;
24
+ }
25
+
26
+ /** Select the one resource owner without creating a second World registry. */
27
+ export function selectCloudLayerFrame(
28
+ candidates: readonly CloudLayerCandidate[],
29
+ worldTimeSeconds = 0,
30
+ sunDirection: readonly [number, number, number] | undefined = undefined,
31
+ sunRadiance: readonly [number, number, number] | undefined = undefined,
32
+ ): Result<ExtractedCloudLayer | undefined, CloudLayerError> {
33
+ if (candidates.length === 0) return ok(undefined);
34
+ if (candidates.length > 1) return err(new CloudLayerOwnerConflictError(candidates.length));
35
+ const candidate = candidates[0];
36
+ if (candidate === undefined) return ok(undefined);
37
+ const validated = validateCloudLayer(candidate.data);
38
+ if (!validated.ok) return validated;
39
+ return ok(
40
+ Object.freeze({
41
+ status: 'available' as const,
42
+ entityKey: candidate.entityKey,
43
+ sourceKey: cloudLayerSourceKey(validated.value),
44
+ params: validated.value,
45
+ worldTimeSeconds: Number.isFinite(worldTimeSeconds) ? Math.max(0, worldTimeSeconds) : 0,
46
+ sunDirection:
47
+ sunDirection === undefined
48
+ ? undefined
49
+ : Object.freeze([...sunDirection] as [number, number, number]),
50
+ sunRadiance:
51
+ sunRadiance === undefined
52
+ ? undefined
53
+ : Object.freeze([...sunRadiance] as [number, number, number]),
54
+ revision: 1,
55
+ }),
56
+ );
57
+ }
58
+
59
+ /** Read the World-owned component and Time resource at the extraction boundary. */
60
+ export function extractCloudLayer(
61
+ world: World,
62
+ ): Result<ExtractedCloudLayer | undefined, CloudLayerError> {
63
+ const candidates: CloudLayerCandidate[] = [];
64
+ // Older worlds may be rendered without renderComponentsPlugin. The absence
65
+ // of the optional cloud vocabulary is the empty state, not an extraction
66
+ // failure; once the component is registered, query failures remain visible
67
+ // through the surrounding World extraction boundary.
68
+ try {
69
+ const query = world.query({ read: [CloudLayer] });
70
+ if (!query.ok) return ok(undefined);
71
+ for (const row of query.value)
72
+ candidates.push({ entityKey: row.entity, data: row.get(CloudLayer) });
73
+ } catch {
74
+ return ok(undefined);
75
+ }
76
+ let sunDirection: readonly [number, number, number] | undefined;
77
+ let sunRadiance: readonly [number, number, number] | undefined;
78
+ try {
79
+ const query = world.query({ read: [DirectionalLight] });
80
+ if (query.ok) {
81
+ for (const row of query.value) {
82
+ const light = row.get(DirectionalLight);
83
+ const intensity = Number.isFinite(light.intensity) ? Math.max(0, light.intensity) : 0;
84
+ // DirectionalLight stores the outgoing vector (light -> surface),
85
+ // while cloud optical paths integrate from the receiver toward the
86
+ // sun. Keep the extracted cloud direction in that incoming convention
87
+ // so low-sun gating and the GPU `-view.lightDir` path agree.
88
+ sunDirection = [
89
+ -(light.direction[0] ?? 0),
90
+ -(light.direction[1] ?? -1),
91
+ -(light.direction[2] ?? 0),
92
+ ];
93
+ sunRadiance = [
94
+ (light.color[0] ?? 1) * intensity,
95
+ (light.color[1] ?? 1) * intensity,
96
+ (light.color[2] ?? 1) * intensity,
97
+ ];
98
+ break;
99
+ }
100
+ }
101
+ } catch {
102
+ // Optional light vocabulary is absent in camera-only worlds.
103
+ }
104
+ return selectCloudLayerFrame(
105
+ candidates,
106
+ world.getResource(Time).elapsed,
107
+ sunDirection,
108
+ sunRadiance,
109
+ );
110
+ }